Skip to content

Commit

Permalink
feat: theme selector
Browse files Browse the repository at this point in the history
  • Loading branch information
MarioRodrigues10 committed Dec 25, 2024
1 parent 716dbdd commit 96c6c34
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 4 deletions.
8 changes: 8 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import live_select from "live_select"
import { QrScanner, Wheel, Confetti, Sorting } from "./hooks";
import darkModeHook from "../vendor/dark_mode"

let Hooks = {
QrScanner: QrScanner,
Expand All @@ -31,6 +32,7 @@ let Hooks = {
Sorting: Sorting,
...live_select
};
Hooks.DarkThemeToggle = darkModeHook

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
Expand All @@ -39,6 +41,12 @@ let liveSocket = new LiveSocket("/live", Socket, {
hooks: Hooks
})

if (localStorage.getItem('theme') === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark')
}

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
Expand Down
2 changes: 1 addition & 1 deletion assets/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const path = require("path")
const colors = require("tailwindcss/colors");

module.exports = {
//darkMode: "selector",
darkMode: 'class',
content: [
"./js/**/*.js",
"../lib/safira_web.ex",
Expand Down
40 changes: 40 additions & 0 deletions assets/vendor/dark_mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

const localStorageKey = 'theme';

const isDark = () => {
if (localStorage.getItem(localStorageKey) === 'dark') return true;
if (localStorage.getItem(localStorageKey) === 'light') return false;
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}

const setupThemeToggle = () => {
toggleVisibility = (dark) => {
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
if (themeToggleDarkIcon == null || themeToggleLightIcon == null) return;
const show = dark ? themeToggleDarkIcon : themeToggleLightIcon
const hide = dark ? themeToggleLightIcon : themeToggleDarkIcon
show.classList.remove('hidden', 'text-transparent');
hide.classList.add('hidden', 'text-transparent');
if (dark) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
try { localStorage.setItem(localStorageKey, dark ? 'dark' : 'light') } catch (_err) { }
}
toggleVisibility(isDark())
document.getElementById('theme-toggle').addEventListener('click', function () {
toggleVisibility(!isDark())
});
}

const darkModeHook = {
mounted() {
setupThemeToggle();
},
updated() {
},
}

export default darkModeHook;
53 changes: 53 additions & 0 deletions lib/safira_web/components/dark_mode.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule DarkMode do
use Phoenix.Component

def button(assigns) do
~H"""
<button
id="theme-toggle"
type="button"
phx-update="ignore"
phx-hook="DarkThemeToggle"
class="text-zinc-400 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700 rounded-lg text-sm p-2"
>
<svg
id="theme-toggle-dark-icon"
class="w-5 h-5 text-transparent hidden"
fill="white"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
</svg>
<svg
id="theme-toggle-light-icon"
class="w-5 h-5 text-transparent"
fill="zinc-400"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
fill-rule="evenodd"
clip-rule="evenodd"
>
</path>
</svg>
</button>
<script>
// Toggle early based on <html class="dark">
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
if (themeToggleDarkIcon != null && themeToggleLightIcon != null) {
let dark = document.documentElement.classList.contains('dark');
const show = dark ? themeToggleDarkIcon : themeToggleLightIcon
const hide = dark ? themeToggleLightIcon : themeToggleDarkIcon
show.classList.remove('hidden', 'text-transparent');
hide.classList.add('hidden', 'text-transparent');
}
</script>
"""
end
end
2 changes: 1 addition & 1 deletion lib/safira_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Expand Down
2 changes: 1 addition & 1 deletion lib/safira_web/components/page.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule SafiraWeb.Components.Page do
<.header title_class={"#{size_class(@size)} #{@title_class}"}>
<%= @title %>
<:subtitle>
<%= @subtitle %>
<span class="text-black dark:text-white"><%= @subtitle %></span>
</:subtitle>
<%= if @back_to_link do %>
<.link patch={@back_to_link}>
Expand Down
2 changes: 1 addition & 1 deletion lib/safira_web/components/sidebar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ defmodule SafiraWeb.Components.Sidebar do
<:img src={"https://github.com/identicons/#{@user.handle |> String.slice(0..2)}.png"} />
<:title color={@title_color}><%= @user.name %></:title>
<:subtitle color={@subtitle_color}>@<%= @user.handle %></:subtitle>
<:link navigate="/profile/settings">Settings</:link>
<:link navigate="/app/profile/settings">Settings</:link>
<:link href="/users/log_out" method={:delete}>Sign out</:link>
</.user_dropdown>
"""
Expand Down
27 changes: 27 additions & 0 deletions lib/safira_web/live/app/profile_live/form_component.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule SafiraWeb.App.ProfileLive.FormComponent do
use SafiraWeb, :live_component

@impl true
def render(assigns) do
~H"""
<div>
<.page
title="Options"
subtitle={gettext("Customize your profile.")}
title_class="text-2xl text-black dark:text-white"
>
<div class="flex flex-col md:flex-row w-full gap-4 dark:text-zinc-400">
<div class="w-full space-y-2">
<DarkMode.button />
</div>
</div>
</.page>
</div>
"""
end

@impl true
def mount(socket) do
{:ok, socket}
end
end
27 changes: 27 additions & 0 deletions lib/safira_web/live/app/profile_live/index.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule SafiraWeb.App.ProfileLive.Index do
use SafiraWeb, :app_view

@impl true
def mount(_params, _session, socket) do
{:ok, socket}
end

@impl true
def handle_params(_params, _, socket) do
{:noreply,
socket
|> assign(:user, socket.assigns.current_user)
|> assign(:current_page, :profile)
|> apply_action(socket.assigns.live_action, _params)}
end

defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Profile")
end

defp apply_action(socket, :edit, _params) do
socket
|> assign(:page_title, "Edit Profile")
end
end
18 changes: 18 additions & 0 deletions lib/safira_web/live/app/profile_live/index.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<.page id="profile" title="Profile">
<:actions>
<.button>
<.icon name="hero-edit" class="w-5 h-5" />
</.button>
</:actions>
</.page>

<.modal :if={@live_action in [:edit]} id="profile-modal" show on_cancel={JS.patch(~p"/")}>
<.live_component
module={SafiraWeb.App.ProfileLive.FormComponent}
id="profile"
current_user={@current_user}
action={@live_action}
patch={~p"/"}
page_title={@page_title}
/>
</.modal>
5 changes: 5 additions & 0 deletions lib/safira_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ defmodule SafiraWeb.Router do
live "/product/:id", Show, :show
end

scope "/profile", ProfileLive do
live "/", Index, :index
live "/settings", Index, :edit
end

live "/vault", VaultLive.Index, :index
end

Expand Down

0 comments on commit 96c6c34

Please sign in to comment.