Skip to content

Commit

Permalink
feat: added create and edit base support to rubrics view
Browse files Browse the repository at this point in the history
- created `LantternWeb.RubricsLive.FormComponent` live component and added support to edit/create rubric in `LantternWeb.RubricsLive.Explorer`

- added `get_button_styles/1` function to core components, to apply button styles to different elements (e.g. links)

- created `<.toggle>` core component

- added support to `<.input type="toggle">` (basically a checkbox "mask")

- added support to custom labels in `<.input>` component through `<:custom_label>` slot

- added support to `:prevent_close_on_click_away` attr on `<.slide_over>` component
  • Loading branch information
endoooo committed Nov 2, 2023
1 parent cd6cabc commit c90fbd3
Show file tree
Hide file tree
Showing 6 changed files with 489 additions and 19 deletions.
60 changes: 57 additions & 3 deletions lib/lanttern_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ defmodule LantternWeb.CoreComponents do
attr :type, :string, default: nil
attr :class, :any, default: nil
attr :theme, :string, default: "default", doc: "default | ghost"
attr :icon_name, :string, default: nil
attr :rest, :global, include: ~w(disabled form name value)

slot :inner_block, required: true
Expand All @@ -161,18 +162,36 @@ defmodule LantternWeb.CoreComponents do
<button
type={@type}
class={[
"rounded-sm py-2 px-2 font-display text-sm font-bold",
"phx-submit-loading:opacity-50 phx-click-loading:opacity-50 phx-click-loading:pointer-events-none",
button_theme(@theme),
get_button_styles(@theme),
@class
]}
{@rest}
>
<%= render_slot(@inner_block) %>
<%= if @icon_name do %>
<.icon name={@icon_name} class="w-5 h-5" />
<% end %>
</button>
"""
end

@doc """
Returns a list of button styles.
Meant to be used while styling links as buttons.
## Examples
<.link patch={~p"/somepath"} class={[get_button_styles()]}>Link</.link>
"""
def get_button_styles(theme \\ "default") do
[
"inline-flex items-center gap-1 rounded-sm py-2 px-2 font-display text-sm font-bold",
"phx-submit-loading:opacity-50 phx-click-loading:opacity-50 phx-click-loading:pointer-events-none",
button_theme(theme)
]
end

defp button_theme(theme) do
%{
"default" => "bg-ltrn-primary hover:bg-cyan-300 shadow-sm",
Expand Down Expand Up @@ -595,6 +614,41 @@ defmodule LantternWeb.CoreComponents do
"""
end

@doc """
Toggle component.
"""

attr :enabled, :boolean, required: true
attr :class, :any, default: nil
attr :sr_text, :string, default: nil
attr :rest, :global

def toggle(assigns) do
~H"""
<button
type="button"
class={[
"relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-ltrn-primary focus:ring-offset-2",
if(@enabled, do: "bg-ltrn-primary", else: "bg-ltrn-lighter"),
@class
]}
role="switch"
aria-checked="false"
{@rest}
>
<span :if={@sr_text} class="sr-only"><%= @sr_text %></span>
<span
aria-hidden="true"
class={[
"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out",
if(@enabled, do: "translate-x-5", else: "translate-x-0")
]}
>
</span>
</button>
"""
end

@doc """
Highlights entring (mounting) elements in DOM.
Expand Down
62 changes: 58 additions & 4 deletions lib/lanttern_web/components/form_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule LantternWeb.FormComponents do
use Phoenix.Component

import LantternWeb.CoreComponents
alias Phoenix.LiveView.JS

@doc """
Renders a simple form.
Expand Down Expand Up @@ -75,7 +76,7 @@ defmodule LantternWeb.FormComponents do
attr :type, :string,
default: "text",
values: ~w(checkbox color date datetime-local email file hidden month number password
range radio search select tel text textarea time url week)
range radio search select tel text textarea time url week toggle)

attr :field, Phoenix.HTML.FormField,
doc: "a form field struct retrieved from the form, for example: @form[:email]"
Expand All @@ -93,6 +94,7 @@ defmodule LantternWeb.FormComponents do
multiple pattern placeholder readonly required rows size step)

slot :inner_block
slot :custom_label

def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do
assigns
Expand Down Expand Up @@ -127,10 +129,41 @@ defmodule LantternWeb.FormComponents do
"""
end

def input(%{type: "toggle", value: value} = assigns) do
assigns =
assign_new(assigns, :checked, fn -> Phoenix.HTML.Form.normalize_value("checkbox", value) end)

~H"""
<div phx-feedback-for={@name} class={@class}>
<label id={"#{@id}-label"} class="flex items-center gap-4 text-sm leading-6 text-zinc-600">
<input type="hidden" name={@name} value="false" />
<input
type="checkbox"
id={@id}
name={@name}
value="true"
checked={@checked}
class="hidden"
{@rest}
/>
<.toggle enabled={@checked} phx-click={JS.dispatch("click", to: "##{@id}-label")} />
<%= @label %>
</label>
<.error :for={msg <- @errors}><%= msg %></.error>
</div>
"""
end

def input(%{type: "select"} = assigns) do
~H"""
<div phx-feedback-for={@name} class={@class}>
<.label for={@id} show_optional={@show_optional}><%= @label %></.label>
<.label
for={@id}
show_optional={@show_optional}
custom={if @custom_label, do: true, else: false}
>
<%= @label || render_slot(@custom_label) %>
</.label>
<.select
id={@id}
name={@name}
Expand All @@ -148,7 +181,13 @@ defmodule LantternWeb.FormComponents do
def input(%{type: "textarea"} = assigns) do
~H"""
<div phx-feedback-for={@name} class={@class}>
<.label for={@id} show_optional={@show_optional}><%= @label %></.label>
<.label
for={@id}
show_optional={@show_optional}
custom={if @custom_label, do: true, else: false}
>
<%= @label || render_slot(@custom_label) %>
</.label>
<.textarea id={@id} name={@name} errors={@errors} value={@value} {@rest} />
<.error :for={msg <- @errors}><%= msg %></.error>
</div>
Expand All @@ -159,7 +198,13 @@ defmodule LantternWeb.FormComponents do
def input(assigns) do
~H"""
<div phx-feedback-for={@name} class={@class}>
<.label for={@id} show_optional={@show_optional}><%= @label %></.label>
<.label
for={@id}
show_optional={@show_optional}
custom={if @custom_label, do: true, else: false}
>
<%= @label || render_slot(@custom_label) %>
</.label>
<.base_input type={@type} name={@name} id={@id} value={@value} errors={@errors} {@rest} />
<.error :for={msg <- @errors}><%= msg %></.error>
</div>
Expand All @@ -171,6 +216,7 @@ defmodule LantternWeb.FormComponents do
"""
attr :for, :string, default: nil
attr :show_optional, :boolean, default: false
attr :custom, :boolean, default: false
slot :inner_block, required: true

def label(%{show_optional: true} = assigns) do
Expand All @@ -184,6 +230,14 @@ defmodule LantternWeb.FormComponents do
"""
end

def label(%{custom: true} = assigns) do
~H"""
<label for={@for} class="block mb-2">
<%= render_slot(@inner_block) %>
</label>
"""
end

def label(assigns) do
~H"""
<label for={@for} class="block mb-2 text-sm font-bold">
Expand Down
5 changes: 4 additions & 1 deletion lib/lanttern_web/components/overlay_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ defmodule LantternWeb.OverlayComponents do
attr :id, :string, required: true
attr :show, :boolean, default: false
attr :on_cancel, JS, default: %JS{}
attr :prevent_close_on_click_away, :boolean, default: false

slot :title, required: true
slot :inner_block, required: true
Expand Down Expand Up @@ -180,7 +181,9 @@ defmodule LantternWeb.OverlayComponents do
id={"#{@id}-container"}
phx-window-keydown={JS.exec("data-cancel", to: "##{@id}")}
phx-key="escape"
phx-click-away={JS.exec("data-cancel", to: "##{@id}")}
phx-click-away={
if not @prevent_close_on_click_away, do: JS.exec("data-cancel", to: "##{@id}")
}
class="pointer-events-auto w-screen max-w-xl transition-translate"
>
<div class="flex h-full flex-col divide-y divide-ltrn-lighter bg-white shadow-xl rounded-l">
Expand Down
65 changes: 55 additions & 10 deletions lib/lanttern_web/live/rubrics_live/explorer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule LantternWeb.RubricsLive.Explorer do
use LantternWeb, :live_view

alias Lanttern.Rubrics
alias Lanttern.Rubrics.Rubric

def render(assigns) do
~H"""
Expand All @@ -20,9 +21,12 @@ defmodule LantternWeb.RubricsLive.Explorer do
Showing <%= @results %> results
</p>
</div>
<button class="shrink-0 flex items-center gap-2 text-sm text-ltrn-subtle hover:underline">
<.link
class="shrink-0 flex items-center gap-2 text-sm text-ltrn-subtle hover:underline"
patch={~p"/rubrics/new"}
>
Create rubric <.icon name="hero-plus-mini" class="text-ltrn-primary" />
</button>
</.link>
</div>
<div id="rubrics-list" phx-update="stream">
<div
Expand All @@ -32,15 +36,19 @@ defmodule LantternWeb.RubricsLive.Explorer do
>
<div class="flex items-start justify-between mb-4">
<div class="flex-1">
<p class="font-display font-black text-lg">Criteria: <%= rubric.criteria %></p>
<p class="flex items-center gap-2">
<.badge>#<%= rubric.id %></.badge>
<span class="font-display font-black text-lg">Criteria: <%= rubric.criteria %></span>
</p>
<div class="flex items-center gap-4 mt-4 text-base">
<.icon name="hero-view-columns" class="shrink-0 text-rose-500" /> <%= rubric.scale.name %>
</div>
</div>
<div class="shrink-0 flex items-center gap-2">
<.button type="button" theme="ghost">Id #<%= rubric.id %></.button>
<.button type="button" theme="ghost">Edit</.button>
<.badge :if={rubric.is_differentiation} theme="secondary">Differentiation</.badge>
<.link patch={~p"/rubrics/#{rubric}/edit"} class={get_button_styles("ghost")}>
Edit
</.link>
</div>
</div>
<div class="flex items-stretch gap-2">
Expand All @@ -64,16 +72,35 @@ defmodule LantternWeb.RubricsLive.Explorer do
</div>
</div>
</div>
<.slide_over
:if={@live_action in [:new, :edit]}
id="rubric-form-overlay"
show={true}
prevent_close_on_click_away
>
<:title><%= @overlay_title %></:title>
<.live_component
module={LantternWeb.RubricsLive.FormComponent}
id={@rubric.id || :new}
action={@live_action}
rubric={@rubric}
patch={~p"/rubrics"}
/>
<:actions>
<.button type="button" theme="ghost" phx-click={JS.patch(~p"/rubrics")}>
Cancel
</.button>
<.button type="submit" form="rubric-form" phx-disable-with="Saving...">
Save
</.button>
</:actions>
</.slide_over>
"""
end

# lifecycle

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

def handle_params(_params, _uri, socket) do
rubrics = Rubrics.list_full_rubrics()
results = length(rubrics)

Expand All @@ -82,6 +109,24 @@ defmodule LantternWeb.RubricsLive.Explorer do
|> stream(:rubrics, rubrics)
|> assign(:results, results)

{:noreply, socket}
{:ok, socket}
end

def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end

defp apply_action(socket, :edit, %{"id" => id}) do
socket
|> assign(:overlay_title, "Edit rubric")
|> assign(:rubric, Rubrics.get_rubric!(id, preloads: :descriptors))
end

defp apply_action(socket, :new, _params) do
socket
|> assign(:overlay_title, "Create Rubric")
|> assign(:rubric, %Rubric{})
end

defp apply_action(socket, :index, _params), do: socket
end
Loading

0 comments on commit c90fbd3

Please sign in to comment.