Packages
surface
0.7.2
0.12.3
0.12.2
0.12.1
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha.0
A component based library for Phoenix LiveView
Current section
Files
Jump to
Current section
Files
lib/surface/live_component.ex
defmodule Surface.LiveComponent do
@moduledoc """
A live stateful component. A wrapper around `Phoenix.LiveComponent`.
## Example
defmodule Dialog do
use Surface.LiveComponent
prop title, :string, required: true
def mount(socket) do
{:ok, assign(socket, show: false)}
end
def render(assigns) do
~F"\""
<div class={"modal", "is-active": @show}>
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">{@title}</p>
</header>
<section class="modal-card-body">
<#slot/>
</section>
<footer class="modal-card-foot" style="justify-content: flex-end">
<Button click="hide">Ok</Button>
</footer>
</div>
</div>
"\""
end
# Public API
def show(dialog_id) do
send_update(__MODULE__, id: dialog_id, show: true)
end
# Event handlers
def handle_event("show", _, socket) do
{:noreply, assign(socket, show: true)}
end
def handle_event("hide", _, socket) do
{:noreply, assign(socket, show: false)}
end
end
"""
alias Surface.BaseComponent
defmacro __using__(_) do
quote do
@before_compile Surface.Renderer
use Phoenix.LiveComponent
import Phoenix.LiveView.Helpers, except: [slot: 2]
use Surface.BaseComponent, type: unquote(__MODULE__)
@before_compile unquote(__MODULE__)
use Surface.API, include: [:prop, :slot, :data]
import Phoenix.HTML
@before_compile {Surface.BaseComponent, :__before_compile_init_slots__}
alias Surface.Components.{Context, Raw}
alias Surface.Components.Dynamic.Component
alias Surface.Components.Dynamic.LiveComponent
@doc """
The id of the live component (required by LiveView for stateful components).
"""
prop id, :string, required: true
@doc "Built-in assign"
data socket, :struct
@doc "Built-in assign"
data myself, :struct
end
end
defmacro __before_compile__(env) do
[quoted_mount(env), quoted_update(env)]
end
defp quoted_update(env) do
if Module.defines?(env.module, {:update, 2}) do
quote do
defoverridable update: 2
def update(assigns, socket) do
{:ok, socket} = super(assigns, socket)
{:ok, BaseComponent.restore_private_assigns(socket, assigns)}
end
end
end
end
defp quoted_mount(env) do
defaults = env.module |> Surface.API.get_defaults() |> Macro.escape()
if Module.defines?(env.module, {:mount, 1}) do
quote do
defoverridable mount: 1
def mount(socket) do
super(
socket
|> Surface.init()
|> assign(unquote(defaults))
)
end
end
else
quote do
def mount(socket) do
{:ok,
socket
|> Surface.init()
|> assign(unquote(defaults))}
end
end
end
end
end