live_svelte/lib/component.ex

139 lines
2.8 KiB
Elixir
Raw Normal View History

defmodule LiveSvelte do
use Phoenix.Component
2023-02-20 01:46:05 +00:00
import Phoenix.HTML
2023-03-08 18:05:35 +00:00
alias LiveSvelte.Slots
alias LiveSvelte.SSR
attr(
:props,
:map,
default: %{},
doc: "Props to pass to the Svelte component",
examples: [%{foo: "bar"}, %{foo: "bar", baz: 1}, %{list: [], baz: 1, qux: %{a: 1, b: 2}}]
)
attr(
:name,
:string,
required: true,
doc: "Name of the Svelte component",
examples: ["YourComponent", "directory/Example"]
)
attr(
:class,
:string,
default: nil,
doc: "Class to apply to the Svelte component",
examples: ["my-class", "my-class another-class"]
)
attr(
:ssr,
:boolean,
default: true,
doc: "Whether to render the component on the server",
examples: [true, false]
)
slot(:inner_block, doc: "Inner block of the Svelte component")
2023-02-20 01:46:05 +00:00
2023-02-26 02:40:33 +00:00
@doc """
Renders a Svelte component on the server.
"""
2023-02-20 01:46:05 +00:00
def render(assigns) do
init = Map.get(assigns, :__changed__, nil) == nil
2023-02-20 01:46:05 +00:00
2023-03-08 18:05:35 +00:00
slots =
assigns
|> Slots.rendered_slot_map()
|> Slots.js_process()
2023-02-25 20:33:09 +00:00
ssr_code =
2023-03-25 20:50:58 +00:00
if init and Map.get(assigns, :ssr) do
try do
SSR.render(assigns.name, Map.get(assigns, :props, %{}), slots)
rescue
SSR.NodeNotConfigured -> nil
end
2023-02-25 20:33:09 +00:00
end
2023-02-20 01:46:05 +00:00
assigns =
assigns
|> assign(:init, init)
2023-03-08 18:05:35 +00:00
|> assign(:slots, slots)
2023-02-25 20:33:09 +00:00
|> assign(:ssr_render, ssr_code)
2023-02-20 01:46:05 +00:00
~H"""
<script><%= raw(@ssr_render["head"]) %></script>
<div
id={id(@name)}
data-name={@name}
data-props={json(@props)}
data-slots={Slots.base_encode_64(@slots) |> json}
phx-update="ignore"
phx-hook="SvelteHook"
class={@class}
>
<style><%= raw(@ssr_render["css"]["code"]) %></style>
<%= raw(@ssr_render["html"]) %>
</div>
"""
2023-02-20 01:46:05 +00:00
end
defp json(props) do
props
|> Jason.encode()
|> case do
{:ok, encoded} -> encoded
{:error, _} -> ""
end
end
defp id(name), do: "#{name}-#{System.unique_integer([:positive])}"
2023-04-26 17:47:27 +00:00
@doc false
def get_props(assigns) do
prop_keys =
assigns
|> Map.get(:__changed__)
|> Map.keys()
assigns
2023-04-26 19:55:11 +00:00
|> Enum.filter(fn
{:svelte_opts, _v} -> false
{k, _v} -> k in prop_keys
end)
2023-04-26 17:47:27 +00:00
|> Enum.into(%{})
end
2023-04-26 19:55:11 +00:00
@doc false
def get_ssr(assigns) do
case get_in(assigns, [:svelte_opts, :ssr]) do
nil -> true
ssr -> ssr
end
end
2023-04-26 17:47:27 +00:00
@doc false
defmacro sigil_V({:<<>>, _meta, [string]}, []) do
path = "./assets/svelte/_build/#{__CALLER__.module}.svelte"
with :ok <- File.mkdir_p(Path.dirname(path)) do
File.write!(path, string)
end
quote do
~H"""
2023-04-26 19:55:11 +00:00
<LiveSvelte.render
name={"_build/#{__MODULE__}"}
props={get_props(assigns)}
ssr={get_ssr(assigns)}
class={get_in(assigns, [:svelte_opts, :class])}
/>
2023-04-26 17:47:27 +00:00
"""
end
end
2023-02-20 01:46:05 +00:00
end