Add colorhash
This commit is contained in:
parent
c935fbbc42
commit
beea7b0caf
6 changed files with 110 additions and 10 deletions
|
@ -13,7 +13,8 @@ config :freedive,
|
|||
|
||||
config :freedive,
|
||||
features: [
|
||||
register_account: true
|
||||
register_account: true,
|
||||
colorhash: true
|
||||
]
|
||||
|
||||
# Configures the endpoint
|
||||
|
|
|
@ -18,6 +18,12 @@ if config_env() == :prod do
|
|||
config :freedive, :features, register_account: false
|
||||
end
|
||||
|
||||
# Colorhash feature is enabled by default.
|
||||
# To disable, set the COLORHASH_ENABLE environment variable to "false".
|
||||
if System.get_env("COLORHASH_ENABLE") == "false" do
|
||||
config :freedive, :features, colorhash: false
|
||||
end
|
||||
|
||||
database_path =
|
||||
System.get_env("DATABASE_PATH") ||
|
||||
raise """
|
||||
|
|
|
@ -11,6 +11,7 @@ TLS_CERT_PATH="<%= @data_dir %>/tls.crt"
|
|||
|
||||
# Features
|
||||
REGISTER_ACCOUNT_ENABLE="false"
|
||||
COLORHASH_ENABLE="true"
|
||||
|
||||
# Phoenix
|
||||
PHX_SERVER=true
|
||||
|
|
48
lib/freedive/colorhash.ex
Normal file
48
lib/freedive/colorhash.ex
Normal file
|
@ -0,0 +1,48 @@
|
|||
defmodule Freedive.Colorhash do
|
||||
@moduledoc """
|
||||
Given a string returns consistent HSL color.
|
||||
"""
|
||||
alias Freedive.Features
|
||||
|
||||
@default_hsl {0, 0, 0}
|
||||
|
||||
def hsl(input, css: true) do
|
||||
{hue, saturation, lightness} = hsl(input)
|
||||
"hsl(#{hue}, #{saturation}%, #{lightness}%)"
|
||||
end
|
||||
|
||||
def hsl(input) do
|
||||
if Features.enabled?(:colorhash) do
|
||||
input = String.downcase(input)
|
||||
hash = :erlang.phash2(input, 2_147_483_647)
|
||||
|
||||
hue = calculate_hue(hash)
|
||||
saturation = 80 + rem(hash, 21)
|
||||
lightness = 30 + rem(hash, 31)
|
||||
|
||||
{hue, saturation, lightness}
|
||||
else
|
||||
@default_hsl
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Calculates hue avoiding low contrast colors.
|
||||
"""
|
||||
def calculate_hue(hash) do
|
||||
base_hue = rem(hash, 360)
|
||||
|
||||
# Ranges to exclude (yellow to light green, cyan)
|
||||
excluded_ranges = [
|
||||
# yellow
|
||||
{50, 70},
|
||||
# cyan
|
||||
{175, 185},
|
||||
]
|
||||
|
||||
# Adjust the hue to skip over excluded ranges
|
||||
Enum.reduce(excluded_ranges, base_hue, fn {start, stop}, acc_hue ->
|
||||
if acc_hue >= start and acc_hue <= stop, do: stop + (acc_hue - start + 1), else: acc_hue
|
||||
end)
|
||||
end
|
||||
end
|
|
@ -1,5 +1,6 @@
|
|||
defmodule FreediveWeb.HomeLive do
|
||||
use FreediveWeb, :live_view
|
||||
import Freedive.Colorhash
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
|
@ -14,11 +15,19 @@ defmodule FreediveWeb.HomeLive do
|
|||
<a>System</a>
|
||||
</.panel_tabs>
|
||||
<.panel_tabs is-hidden-tablet>
|
||||
<a class="is-active"><Lucideicons.infinity aria-hidden="true" /></a>
|
||||
<a><Lucideicons.binary aria-hidden="true" /></a>
|
||||
<a><Lucideicons.hard_drive aria-hidden="true" /></a>
|
||||
<a><Lucideicons.earth aria-hidden="true" /></a>
|
||||
<a><Lucideicons.bot aria-hidden="true" /></a>
|
||||
<a class="is-active">
|
||||
<Lucideicons.infinity style={"color: #{hsl("all", css: true)}"} aria-hidden="true" />
|
||||
</a>
|
||||
<a>
|
||||
<Lucideicons.binary style={"color: #{hsl("compute", css: true)}"} aria-hidden="true" />
|
||||
</a>
|
||||
<a>
|
||||
<Lucideicons.hard_drive style={"color: #{hsl("storage", css: true)}"} aria-hidden="true" />
|
||||
</a>
|
||||
<a>
|
||||
<Lucideicons.earth style={"color: #{hsl("network", css: true)}"} aria-hidden="true" />
|
||||
</a>
|
||||
<a><Lucideicons.bot style={"color: #{hsl("system", css: true)}"} aria-hidden="true" /></a>
|
||||
</.panel_tabs>
|
||||
|
||||
<.panel_block>
|
||||
|
@ -37,25 +46,37 @@ defmodule FreediveWeb.HomeLive do
|
|||
</.panel_block>
|
||||
<.link patch={~p"/users/settings"} class="panel-block pt-1">
|
||||
<span class="panel-icon">
|
||||
<Lucideicons.user_cog aria-hidden="true" />
|
||||
<Lucideicons.user_cog
|
||||
style={"color: #{hsl("account settings", css: true)}"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
<span class="mt-2 ml-2">Account settings</span>
|
||||
</.link>
|
||||
<.link patch={~p"/services"} class="panel-block pt-1">
|
||||
<span class="panel-icon">
|
||||
<Lucideicons.puzzle aria-hidden="true" />
|
||||
<Lucideicons.puzzle
|
||||
style={"color: #{hsl("system services", css: true)}"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
<div class="mt-2 ml-2">System services</div>
|
||||
</.link>
|
||||
<.link patch={~p"/packages"} class="panel-block pt-1">
|
||||
<span class="panel-icon">
|
||||
<Lucideicons.package aria-hidden="true" />
|
||||
<Lucideicons.package
|
||||
style={"color: #{hsl("system packages", css: true)}"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
<span class="mt-2 ml-2">System packages</span>
|
||||
</.link>
|
||||
<.link patch={~p"/updates"} class="panel-block pt-1">
|
||||
<span class="panel-icon">
|
||||
<Lucideicons.hard_drive_download aria-hidden="true" />
|
||||
<Lucideicons.hard_drive_download
|
||||
style={"color: #{hsl("system software updates", css: true)}"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
<span class="mt-2 ml-2">System software updates</span>
|
||||
</.link>
|
||||
|
|
23
test/freedive/colorhash_test.exs
Normal file
23
test/freedive/colorhash_test.exs
Normal file
|
@ -0,0 +1,23 @@
|
|||
defmodule Freedive.ColorhashTest do
|
||||
use ExUnit.Case
|
||||
|
||||
test "hsl" do
|
||||
assert Freedive.Colorhash.hsl("foo") == {312, 80, 48}
|
||||
assert Freedive.Colorhash.hsl("bar") == {38, 88, 38}
|
||||
assert Freedive.Colorhash.hsl("baz") == {288, 95, 42}
|
||||
end
|
||||
|
||||
test "hsl with css" do
|
||||
assert Freedive.Colorhash.hsl("foo", css: true) == "hsl(312, 80%, 48%)"
|
||||
assert Freedive.Colorhash.hsl("bar", css: true) == "hsl(38, 88%, 38%)"
|
||||
assert Freedive.Colorhash.hsl("baz", css: true) == "hsl(288, 95%, 42%)"
|
||||
end
|
||||
|
||||
test "hsl with disabled feature" do
|
||||
Freedive.Features.disable(:colorhash)
|
||||
assert Freedive.Colorhash.hsl("foo") == {0, 0, 0}
|
||||
assert Freedive.Colorhash.hsl("bar") == {0, 0, 0}
|
||||
assert Freedive.Colorhash.hsl("baz") == {0, 0, 0}
|
||||
Freedive.Features.enable(:colorhash)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue