Skip to content

Commit a4f718a

Browse files
committed
Add color live view!
1 parent e1abb26 commit a4f718a

File tree

7 files changed

+117
-2
lines changed

7 files changed

+117
-2
lines changed

apps/components_guide_web/lib/components_guide_web.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ defmodule ComponentsGuideWeb do
2323

2424
import Plug.Conn
2525
import ComponentsGuideWeb.Gettext
26+
import Phoenix.LiveView.Controller
2627
alias ComponentsGuideWeb.Router.Helpers, as: Routes
2728
end
2829
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
defmodule ComponentsGuideWeb.ColorController do
2+
use ComponentsGuideWeb, :controller
3+
4+
def index(conn, _params) do
5+
live_render(conn, ComponentsGuideWeb.ColorLive)
6+
# render(conn, "index.html")
7+
end
8+
end
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
defmodule ComponentsGuideWeb.ColorLive do
2+
use ComponentsGuideWeb, :live_view
3+
alias ComponentsGuideWeb.StylingHelpers
4+
5+
defmodule State do
6+
defstruct color: {:lab, 50, 100, -128}
7+
8+
def set_color(state = %__MODULE__{}, color), do: %{state | color: color}
9+
10+
def l(%__MODULE__{color: {:lab, l, _, _}}), do: l
11+
def a(%__MODULE__{color: {:lab, _, a, _}}), do: a
12+
def b(%__MODULE__{color: {:lab, _, _, b}}), do: b
13+
14+
def css(%__MODULE__{color: color}), do: StylingHelpers.to_css(color)
15+
16+
defp clamp_0_1(n), do: n |> max(0) |> min(1)
17+
18+
defp to_srgb(%__MODULE__{color: color}) do
19+
{:srgb, r, g, b} = color |> StylingHelpers.convert(:srgb) |> StylingHelpers.clamp()
20+
{r, g, b}
21+
end
22+
23+
def hex(state = %__MODULE__{}) do
24+
{r, g, b} = state |> to_srgb()
25+
26+
digits = [r, g, b]
27+
|> Enum.map(fn c ->
28+
round(c * 255) |> Integer.to_string(16) |> String.pad_leading(2, "0")
29+
end)
30+
|> Enum.join()
31+
32+
"##{digits}"
33+
end
34+
end
35+
36+
def render(assigns) do
37+
swatch_size = 100
38+
{:lab, l, a, b} = assigns.state.color
39+
40+
gradient = Styling.linear_gradient("150grad", [
41+
{:lab, l * 1.5, a * 1.2, b * 0.8},
42+
{:lab, l, a, b},
43+
{:lab, l * 0.5, a * 0.8, b * 1.2},
44+
])
45+
46+
~L"""
47+
<article class="text-2xl max-w-lg mx-auto text-white">
48+
<svg width="<%= swatch_size %>" height="<%= swatch_size %>" viewbox="0 0 1 1">
49+
<rect fill="<%= State.hex(@state) %>" width="1" height="1" />
50+
</svg>
51+
<div style="width: 100px; height: 100px; background-image: <%= gradient %>"></div>
52+
<form phx-change="lab_changed" class="flex flex-col">
53+
<label>
54+
L
55+
<input type=range name=l min="0" max="100" value="<%= State.l(@state) %>" phx-value-component=l>
56+
<span><%= l %></span>
57+
</label>
58+
<label>
59+
a
60+
<input type=range name=a min="-128" max="127" value="<%= State.a(@state) %>" phx-value-component=a>
61+
<span><%= a %></span>
62+
</label>
63+
<label>
64+
b
65+
<input type=range name=b min="-128" max="127" value="<%= State.b(@state) %>" phx-value-component=b>
66+
<span><%= b %></span>
67+
</label>
68+
</form>
69+
<dl>
70+
<dt class="font-bold">Hex:
71+
<dd><%= State.hex(@state) %>
72+
<dt class="font-bold">CSS:
73+
<dd><%= State.css(@state) %>
74+
<dt class="font-bold">Gradient CSS:
75+
<dd><pre class="text-base whitespace-pre-wrap"><code><%= gradient %></code></pre>
76+
</dl>
77+
</article>
78+
"""
79+
end
80+
81+
def mount(_params, _session, socket) do
82+
{:ok, assign(socket, state: %State{})}
83+
end
84+
85+
def handle_event("lab_changed", %{"l" => l, "a" => a, "b" => b}, socket) do
86+
l = l |> String.to_integer()
87+
a = a |> String.to_integer()
88+
b = b |> String.to_integer()
89+
{:noreply, assign(socket, :state, socket.assigns.state |> State.set_color({:lab, l, a, b}))}
90+
end
91+
end

apps/components_guide_web/lib/components_guide_web/router.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ defmodule ComponentsGuideWeb.Router do
2929
AccessibilityFirstController,
3030
only: [:index, :show]
3131

32+
get "/color", ColorController, :index
33+
3234
get "/swiftui", SwiftUIController, :index
3335
get "/react+typescript", ReactTypescriptController, :index
3436

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<h1>Color</h1>
2+
3+
<%= live_render(@conn, ComponentsGuideWeb.ColorLive, session: %{}) %>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
defmodule ComponentsGuideWeb.ColorView do
2+
use ComponentsGuideWeb, :view
3+
end

apps/components_guide_web/lib/components_guide_web/views/styling_helpers.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,14 @@ defmodule ComponentsGuideWeb.StylingHelpers do
6666
convert(input, :xyz) |> convert(:srgb)
6767
end
6868

69-
def to_css({:srgb, r, g, b}) do
69+
defp clamp_0_1(n) when is_number(n), do: n |> max(0) |> min(1)
70+
71+
def clamp({:srgb, r, g, b}) do
72+
{:srgb, r |> clamp_0_1(), g |> clamp_0_1(), b |> clamp_0_1()}
73+
end
74+
75+
def to_css(color = {:srgb, _, _, _}) do
76+
{:srgb, r, g, b} = clamp(color)
7077
"rgba(#{(r * 255) |> round},#{(g * 255) |> round},#{(b * 255) |> round},1)"
7178
end
7279

@@ -87,7 +94,7 @@ defmodule ComponentsGuideWeb.StylingHelpers do
8794
:array.foldr(
8895
fn index, color, list ->
8996
percentage = "#{index / max * 100}%"
90-
["#{color |> to_css} #{percentage}" | list]
97+
["#{color |> to_css()} #{percentage}" | list]
9198
end,
9299
[],
93100
colors_array

0 commit comments

Comments
 (0)