2023-02-23 04:44:27 +00:00
|
|
|
defmodule LiveSvelte do
|
2023-03-14 01:51:16 +00:00
|
|
|
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
|
|
|
|
|
|
2023-03-25 23:38:05 +00:00
|
|
|
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}}]
|
|
|
|
|
)
|
2023-02-28 08:57:47 +00:00
|
|
|
|
2023-03-25 23:38:05 +00:00
|
|
|
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
|
2023-03-14 01:51:16 +00:00
|
|
|
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
|
|
|
|
2023-03-14 01:51:16 +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
|
|
|
|
2023-03-14 01:51:16 +00:00
|
|
|
~H"""
|
2023-03-25 20:25:38 +00:00
|
|
|
<script><%= raw(@ssr_render["head"]) %></script>
|
2023-03-14 02:15:16 +00:00
|
|
|
<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"
|
2023-04-10 20:04:52 +00:00
|
|
|
class={@class}
|
2023-03-14 02:15:16 +00:00
|
|
|
>
|
2023-03-14 01:51:16 +00:00
|
|
|
<style><%= raw(@ssr_render["css"]["code"]) %></style>
|
|
|
|
|
<%= raw(@ssr_render["html"]) %>
|
2023-03-14 02:15:16 +00:00
|
|
|
</div>
|
2023-03-14 01:51:16 +00:00
|
|
|
"""
|
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
|