From 89d9bc0cce020ada664654f18bdfe32b8f5452bb Mon Sep 17 00:00:00 2001
From: Adam Piontek <adam@73k.us>
Date: Sun, 7 Mar 2021 17:17:16 -0500
Subject: [PATCH] implemented floating labels on inputs & selects, with icons

---
 assets/js/app.js                              |  3 +-
 .../live/user/registration.html.leex          | 64 +++++++-------
 .../live/user/reset_password.html.leex        | 58 ++++++-------
 .../live/user/settings/email.html.leex        | 57 +++++++------
 .../live/user/settings/password.html.leex     | 84 +++++++++----------
 .../user_management/form_component.html.leex  | 76 +++++++++--------
 .../live/user_management/index.html.leex      | 10 +--
 .../templates/user_confirmation/new.html.eex  | 10 +--
 .../user_reset_password/new.html.eex          | 15 ++--
 .../templates/user_session/new.html.eex       | 24 +++---
 10 files changed, 199 insertions(+), 202 deletions(-)

diff --git a/assets/js/app.js b/assets/js/app.js
index 66c975dd..b5943bc8 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -17,9 +17,8 @@ import "../node_modules/bootstrap-icons/icons/at.svg"; // email field
 import "../node_modules/bootstrap-icons/icons/key.svg"; // new password field
 import "../node_modules/bootstrap-icons/icons/key-fill.svg"; // pw confirm field
 import "../node_modules/bootstrap-icons/icons/lock.svg"; // current pw field
-import "../node_modules/bootstrap-icons/icons/shield-shaded.svg"; // role
+import "../node_modules/bootstrap-icons/icons/shield.svg"; // role
 // live tables
-import "../node_modules/bootstrap-icons/icons/filter.svg";
 import "../node_modules/bootstrap-icons/icons/backspace.svg"; // clear filter
 import "../node_modules/bootstrap-icons/icons/sort-down-alt.svg";
 import "../node_modules/bootstrap-icons/icons/sort-up-alt.svg";
diff --git a/lib/shift73k_web/live/user/registration.html.leex b/lib/shift73k_web/live/user/registration.html.leex
index f64e057e..511a9666 100644
--- a/lib/shift73k_web/live/user/registration.html.leex
+++ b/lib/shift73k_web/live/user/registration.html.leex
@@ -9,42 +9,42 @@
 
   <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "reg_form"], fn f -> %>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
-      <%= label f, :email, class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-at", [class: "icon"] %>
-        </span>
-        <%= email_input f, :email,
-            value: input_value(f, :email),
-            class: input_class(f, :email, "form-control"),
-            placeholder: "e.g., babka@73k.us",
-            maxlength: User.max_email,
-            autofocus: true,
-            phx_debounce: "blur",
-            aria_describedby: error_ids(f, :email)
-          %>
-        <%= error_tag f, :email %>
-      </div>
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
+      <%= email_input f, :email,
+          value: input_value(f, :email),
+          class: input_class(f, :email, "form-control"),
+          placeholder: "e.g., babka@73k.us",
+          maxlength: User.max_email,
+          autofocus: true,
+          phx_debounce: "blur",
+          aria_describedby: error_ids(f, :email)
+        %>
+      <%= label f, :email do %>
+        <%= icon_div @socket, "bi-at", [class: "icon baseline text-muted"] %>
+        Email
+      <% end %>
+      <%= error_tag f, :email %>
     </div>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
-      <%= label f, :password, class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-key", [class: "icon"] %>
-        </span>
-        <%= password_input f, :password,
-            value: input_value(f, :password),
-            class: input_class(f, :password, "form-control"),
-            maxlength: User.max_password,
-            phx_debounce: "250",
-            aria_describedby: error_ids(f, :password)
-          %>
-        <%= error_tag f, :password %>
-      </div>
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
+      <%= password_input f, :password,
+          value: input_value(f, :password),
+          class: input_class(f, :password, "form-control"),
+          placeholder: "Password",
+          maxlength: User.max_password,
+          phx_debounce: "250",
+          aria_describedby: error_ids(f, :password)
+        %>
+      <%= label f, :password do %>
+        <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %>
+        Password
+      <% end %>
+      <%= error_tag f, :password %>
     </div>
 
+
     <div class="mb-3">
       <%= submit (@trigger_submit && "Saving..." || "Register"),
           class: "btn btn-primary",
diff --git a/lib/shift73k_web/live/user/reset_password.html.leex b/lib/shift73k_web/live/user/reset_password.html.leex
index baae9beb..b29ae36c 100644
--- a/lib/shift73k_web/live/user/reset_password.html.leex
+++ b/lib/shift73k_web/live/user/reset_password.html.leex
@@ -9,39 +9,39 @@
 
   <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "pw_reset_form"], fn f -> %>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
-      <%= label f, :password, "New password", class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-key", [class: "icon"] %>
-        </span>
-        <%= password_input f, :password,
-            value: input_value(f, :password),
-            class: input_class(f, :password, "form-control"),
-            maxlength: User.max_password,
-            autofocus: true,
-            aria_describedby: error_ids(f, :password)
-          %>
-        <%= error_tag f, :password %>
-      </div>
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
+      <%= password_input f, :password,
+          value: input_value(f, :password),
+          class: input_class(f, :password, "form-control"),
+          placeholder: "New Password",
+          maxlength: User.max_password,
+          autofocus: true,
+          aria_describedby: error_ids(f, :password)
+        %>
+      <%= label f, :password do %>
+        <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %>
+        New password
+      <% end %>
+      <%= error_tag f, :password %>
     </div>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
-      <%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-key-fill", [class: "icon"] %>
-        </span>
-        <%= password_input f, :password_confirmation,
-            value: input_value(f, :password_confirmation),
-            class: input_class(f, :password_confirmation, "form-control"),
-            maxlength: User.max_password,
-            aria_describedby: error_ids(f, :password_confirmation)
-          %>
-        <%= error_tag f, :password_confirmation %>
-      </div>
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
+      <%= password_input f, :password_confirmation,
+          value: input_value(f, :password_confirmation),
+          class: input_class(f, :password_confirmation, "form-control"),
+          placeholder: "Confirm new password",
+          maxlength: User.max_password,
+          aria_describedby: error_ids(f, :password_confirmation)
+        %>
+      <%= label f, :password do %>
+        <%= icon_div @socket, "bi-key-fill", [class: "icon baseline text-muted"] %>
+        Confirm new password
+      <% end %>
+      <%= error_tag f, :password_confirmation %>
     </div>
 
+
     <div class="mb-3">
       <%= submit "Reset password",
           class: "btn btn-primary",
diff --git a/lib/shift73k_web/live/user/settings/email.html.leex b/lib/shift73k_web/live/user/settings/email.html.leex
index 8016cd85..2d1b6e47 100644
--- a/lib/shift73k_web/live/user/settings/email.html.leex
+++ b/lib/shift73k_web/live/user/settings/email.html.leex
@@ -4,40 +4,39 @@
 
   <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
-      <%= label f, :email, class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-at", [class: "icon"] %>
-        </span>
-        <%= email_input f, :email,
-            value: input_value(f, :email),
-            class: input_class(f, :email, "form-control"),
-            placeholder: "e.g., babka@73k.us",
-            maxlength: User.max_email,
-            phx_debounce: "500",
-            aria_describedby: error_ids(f, :email)
-          %>
-        <%= error_tag f, :email %>
-      </div>
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
+      <%= email_input f, :email,
+          value: input_value(f, :email),
+          class: input_class(f, :email, "form-control"),
+          placeholder: "e.g., babka@73k.us",
+          maxlength: User.max_email,
+          phx_debounce: "500",
+          aria_describedby: error_ids(f, :email)
+        %>
+      <%= label f, :email do %>
+        <%= icon_div @socket, "bi-at", [class: "icon baseline text-muted"] %>
+        Email
+      <% end %>
+      <%= error_tag f, :email %>
     </div>
 
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
-      <%= label f, :current_password, class: "form-label" %>
-      <div class="input-group">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-lock", [class: "icon"] %>
-        </span>
-        <%= password_input f, :current_password,
-            value: input_value(f, :current_password),
-            class: "form-control",
-            aria_describedby: error_ids(f, :current_password)
-          %>
-        <%= error_tag f, :current_password %>
-      </div>
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
+      <%= password_input f, :current_password,
+          value: input_value(f, :current_password),
+          class: "form-control",
+          placeholder: "Current Password",
+          aria_describedby: error_ids(f, :current_password)
+        %>
+      <%= label f, :current_password do %>
+        <%= icon_div @socket, "bi-lock", [class: "icon baseline text-muted"] %>
+        Current password
+      <% end %>
+      <%= error_tag f, :current_password %>
     </div>
 
+
     <div class="mb-3">
       <%= submit "Change email",
           class: "btn btn-primary",
diff --git a/lib/shift73k_web/live/user/settings/password.html.leex b/lib/shift73k_web/live/user/settings/password.html.leex
index b216f15c..6d781dfd 100644
--- a/lib/shift73k_web/live/user/settings/password.html.leex
+++ b/lib/shift73k_web/live/user/settings/password.html.leex
@@ -4,52 +4,52 @@
 
   <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
-      <%= label f, :password, "New password", class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-key", [class: "icon"] %>
-        </span>
-        <%= password_input f, :password,
-            value: input_value(f, :password),
-            class: input_class(f, :password, "form-control"),
-            maxlength: User.max_password,
-            phx_debounce: "500",
-            aria_describedby: error_ids(f, :password)
-          %>
-        <%= error_tag f, :password %>
-      </div>
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
+      <%= password_input f, :password,
+          value: input_value(f, :password),
+          class: input_class(f, :password, "form-control"),
+          placeholder: "New Password",
+          maxlength: User.max_password,
+          phx_debounce: "500",
+          aria_describedby: error_ids(f, :password)
+        %>
+      <%= label f, :password do %>
+        <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %>
+        New password
+      <% end %>
+      <%= error_tag f, :password %>
     </div>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
-      <%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-key-fill", [class: "icon"] %>
-        </span>
-        <%= password_input f, :password_confirmation,
-            value: input_value(f, :password_confirmation),
-            class: input_class(f, :password_confirmation, "form-control"),
-            maxlength: User.max_password,
-            aria_describedby: error_ids(f, :password_confirmation)
-          %>
-        <%= error_tag f, :password_confirmation %>
-      </div>
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
+      <%= password_input f, :password_confirmation,
+          value: input_value(f, :password_confirmation),
+          class: input_class(f, :password_confirmation, "form-control"),
+          placeholder: "Confirm new password",
+          maxlength: User.max_password,
+          aria_describedby: error_ids(f, :password_confirmation)
+        %>
+      <%= label f, :password_confirmation do %>
+        <%= icon_div @socket, "bi-key-fill", [class: "icon baseline text-muted"] %>
+        Confirm new password
+      <% end %>
+      <%= error_tag f, :password_confirmation %>
     </div>
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
-      <%= label f, :current_password, class: "form-label" %>
-      <div class="input-group">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-lock", [class: "icon"] %>
-        </span>
-        <%= password_input f, :current_password,
-            value: input_value(f, :current_password),
-            class: "form-control",
-            aria_describedby: error_ids(f, :current_password)
-          %>
-        <%= error_tag f, :current_password %>
-      </div>
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
+      <%= password_input f, :current_password,
+          value: input_value(f, :current_password),
+          class: "form-control",
+          placeholder: "Current Password",
+          aria_describedby: error_ids(f, :current_password)
+        %>
+      <%= label f, :current_password do %>
+        <%= icon_div @socket, "bi-lock", [class: "icon baseline text-muted"] %>
+        Current password
+      <% end %>
+      <%= error_tag f, :current_password %>
     </div>
 
     <div class="mb-3">
diff --git a/lib/shift73k_web/live/user_management/form_component.html.leex b/lib/shift73k_web/live/user_management/form_component.html.leex
index 92a10f7e..65607c0c 100644
--- a/lib/shift73k_web/live/user_management/form_component.html.leex
+++ b/lib/shift73k_web/live/user_management/form_component.html.leex
@@ -6,32 +6,49 @@
 
   <div class="modal-body">
 
-    <div class="mb-3" phx-feedback-for="<%= input_id(f, :email)%>">
-      <%= label f, :email, class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-at", [class: "icon"] %>
-        </span>
-        <%= email_input f, :email,
-            value: input_value(f, :email),
-            class: input_class(f, :email, "form-control"),
-            placeholder: "e.g., babka@73k.us",
-            maxlength: User.max_email,
-            autofocus: true,
-            phx_debounce: "250",
-            aria_describedby: error_ids(f, :email)
-          %>
-        <%= error_tag f, :email %>
-      </div>
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
+      <%= email_input f, :email,
+          value: input_value(f, :email),
+          class: input_class(f, :email, "form-control"),
+          placeholder: "e.g., babka@73k.us",
+          maxlength: User.max_email,
+          autofocus: true,
+          phx_debounce: "250",
+          aria_describedby: error_ids(f, :email)
+        %>
+      <%= label f, :email do %>
+        <%= icon_div @socket, "bi-at", [class: "icon baseline text-muted"] %>
+        Email
+      <% end %>
+      <%= error_tag f, :email %>
     </div>
 
+
+    <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
+      <%= password_input f, :password,
+          value: input_value(f, :password),
+          class: input_class(f, :password, "form-control"),
+          placeholder: "Password",
+          maxlength: User.max_password,
+          aria_describedby: error_ids(f, :password)
+        %>
+      <%= label f, :password do %>
+        <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %>
+        Password
+      <% end %>
+      <%= error_tag f, :password %>
+    </div>
+
+
+
+
     <%= if Roles.can?(@current_user, %User{}, :edit_role) do %>
-      <%= label f, :role, class: "form-label" %>
-      <div class="input-group mb-3">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-shield-shaded", [class: "icon"] %>
-        </span>
+      <div class="form-floating">
         <%= select f, :role, Enum.map(User.roles(), fn {k, _v} -> {String.capitalize(Atom.to_string(k)), k} end), class: "form-select" %>
+        <%= label f, :role do %>
+          <%= icon_div @socket, "bi-shield", [class: "icon baseline text-muted"] %>
+          User role
+        <% end %>
         <span class="valid-feedback text-primary" style="display: block;">
           <%= role_description(input_value(f, :role)) %>
         </span>
@@ -40,21 +57,6 @@
       <%= hidden_input f, :role, value: input_value(f, :role) %>
     <% end %>
 
-    <div phx-feedback-for="<%= input_id(f, :password) %>">
-      <%= label f, :password, class: "form-label" %>
-      <div class="input-group has-validation">
-        <span class="input-group-text">
-          <%= icon_div @socket, "bi-key", [class: "icon"] %>
-        </span>
-        <%= password_input f, :password,
-            value: input_value(f, :password),
-            class: input_class(f, :password, "form-control"),
-            maxlength: User.max_password,
-            aria_describedby: error_ids(f, :password)
-          %>
-        <%= error_tag f, :password %>
-      </div>
-    </div>
 
   </div>
   <div class="modal-footer">
diff --git a/lib/shift73k_web/live/user_management/index.html.leex b/lib/shift73k_web/live/user_management/index.html.leex
index ef589c5e..d36fb7ab 100644
--- a/lib/shift73k_web/live/user_management/index.html.leex
+++ b/lib/shift73k_web/live/user_management/index.html.leex
@@ -33,18 +33,12 @@
   <%= form_for :filter, "#", [phx_change: "filter-change"], fn flt -> %>
   <div class="input-group">
 
-    <span class="input-group-text">
-      <%= icon_div @socket, "bi-filter", [class: "icon"] %>
-    </span>
-
     <%= text_input flt, :filter, name: "filter", class: "form-control", placeholder: "Filter users...", value: @query.filter %>
 
-    </button>
-
     <%= if @query.filter == "" do %>
-      <button class="btn btn-outline-secondary" type="button" aria-label="Clear filter" aria-disabled="true" disabled>
+    <button class="btn btn-outline-primary" type="button" aria-label="Clear filter" aria-disabled="true" disabled>
     <% else %>
-      <button class="btn btn-outline-secondary" type="button" aria-label="Clear filter" phx-click="filter-clear">
+    <button class="btn btn-primary" type="button" aria-label="Clear filter" phx-click="filter-clear">
     <% end %>
       <%= icon_div @socket, "bi-backspace", [class: "icon baseline"], [role: "img", aria_hidden: false] %>
     </button>
diff --git a/lib/shift73k_web/templates/user_confirmation/new.html.eex b/lib/shift73k_web/templates/user_confirmation/new.html.eex
index d2356fa1..5053dad7 100644
--- a/lib/shift73k_web/templates/user_confirmation/new.html.eex
+++ b/lib/shift73k_web/templates/user_confirmation/new.html.eex
@@ -9,11 +9,7 @@
 
   <%= form_for :user, Routes.user_confirmation_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
 
-    <%= label f, :email, class: "form-label" %>
-    <div class="input-group has-validation mb-3">
-      <span class="input-group-text">
-        <%= icon_div @conn, "bi-at", [class: "icon fs-5"] %>
-      </span>
+    <div class="form-floating mb-3">
       <%= email_input f, :email,
           value: @current_user && @current_user.email || "",
           placeholder: "e.g., babka@73k.us",
@@ -22,6 +18,10 @@
           required: true,
           autofocus: !@current_user
         %>
+      <%= label f, :email do %>
+        <%= icon_div @conn, "bi-at", [class: "icon baseline text-muted"] %>
+        Email
+      <% end %>
       <span class="invalid-feedback">must be a valid email address</span>
     </div>
 
diff --git a/lib/shift73k_web/templates/user_reset_password/new.html.eex b/lib/shift73k_web/templates/user_reset_password/new.html.eex
index f5a4aa5b..aa2f5476 100644
--- a/lib/shift73k_web/templates/user_reset_password/new.html.eex
+++ b/lib/shift73k_web/templates/user_reset_password/new.html.eex
@@ -9,18 +9,17 @@
 
   <%= form_for :user, Routes.user_reset_password_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
 
-    <%= label f, :email, class: "form-label" %>
-    <div class="input-group has-validation mb-3">
-      <span class="input-group-text">
-        <%= icon_div @conn, "bi-at", [class: "icon"] %>
-      </span>
+    <div class="form-floating mb-3">
       <%= email_input f, :email,
-          placeholder: "e.g., babka@73k.us",
           class: "form-control",
+          placeholder: "e.g., babka@73k.us",
           maxlength: User.max_email,
-          required: true,
-          autofocus: true
+          required: true
         %>
+      <%= label f, :email do %>
+        <%= icon_div @conn, "bi-at", [class: "icon baseline text-muted"] %>
+        Email
+      <% end %>
       <span class="invalid-feedback">must be a valid email address</span>
     </div>
 
diff --git a/lib/shift73k_web/templates/user_session/new.html.eex b/lib/shift73k_web/templates/user_session/new.html.eex
index ad58e38e..7b62f589 100644
--- a/lib/shift73k_web/templates/user_session/new.html.eex
+++ b/lib/shift73k_web/templates/user_session/new.html.eex
@@ -15,32 +15,36 @@
       </div>
     <% end %>
 
-    <%= label f, :email, class: "form-label" %>
-    <div class="input-group has-validation mb-3">
-      <span class="input-group-text">
-        <%= icon_div @conn, "bi-at", [class: "icon"] %>
-      </span>
+
+    <div class="form-floating mb-3">
       <%= email_input f, :email,
           class: "form-control",
           placeholder: "e.g., babka@73k.us",
           maxlength: User.max_email,
           required: true
         %>
+      <%= label f, :email do %>
+        <%= icon_div @conn, "bi-at", [class: "icon baseline text-muted"] %>
+        Email
+      <% end %>
       <span class="invalid-feedback">must be a valid email address</span>
     </div>
 
-    <%= label f, :password, class: "form-label" %>
-    <div class="input-group has-validation mb-3">
-      <span class="input-group-text">
-        <%= icon_div @conn, "bi-lock", [class: "icon"] %>
-      </span>
+
+    <div class="form-floating mb-3">
       <%= password_input f, :password,
           class: "form-control",
+          placeholder: "Password",
           required: true
         %>
+      <%= label f, :password do %>
+        <%= icon_div @conn, "bi-lock", [class: "icon baseline text-muted"] %>
+        Password
+      <% end %>
       <span class="invalid-feedback">password is required</span>
     </div>
 
+
     <div class="form-check mb-3 no-valid-style">
       <%= checkbox f, :remember_me, class: "form-check-input" %>
       <%= label f, :remember_me, "Keep me logged in for 60 days", class: "form-check-label" %>