Skip to content

Commit

Permalink
Merge pull request #77 from liveview-native/layout-and-stylesheet-fixes
Browse files Browse the repository at this point in the history
Fix various layout and stylesheet bugs
  • Loading branch information
supernintendo authored Dec 8, 2023
2 parents 4b2ce02 + b346270 commit 3fe5e43
Show file tree
Hide file tree
Showing 17 changed files with 289 additions and 91 deletions.
6 changes: 5 additions & 1 deletion lib/live_view_native/extensions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ defmodule LiveViewNative.Extensions do
respectively.
"""
defmacro __using__(opts) do
compiled_at = :os.system_time(:nanosecond)
role = opts[:role]

quote bind_quoted: [caller: Macro.escape(__CALLER__), role: role], location: :keep do
quote bind_quoted: [caller: Macro.escape(__CALLER__), compiled_at: compiled_at, role: role],
location: :keep do
Code.put_compiler_option(:ignore_module_conflict, true)

for {platform_id, platform_context} <- LiveViewNative.platforms() do
Expand Down Expand Up @@ -43,10 +45,12 @@ defmodule LiveViewNative.Extensions do

if is_nil(platform_context.render_macro) do
use LiveViewNative.Extensions.InlineRender,
compiled_at: compiled_at,
platform_id: platform_id,
role: role
else
use LiveViewNative.Extensions.RenderMacro,
compiled_at: compiled_at,
platform_id: platform_id,
render_macro: platform_context.render_macro,
role: role
Expand Down
5 changes: 4 additions & 1 deletion lib/live_view_native/extensions/inline_render.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ defmodule LiveViewNative.Extensions.InlineRender do
"""
defmacro __using__(opts \\ []) do
quote bind_quoted: [
compiled_at: opts[:compiled_at],
platform_id: opts[:platform_id],
stylesheet: opts[:stylesheet],
role: opts[:role]
], location: :keep do
],
location: :keep do
require EEx

defmacro sigil_LVN({:<<>>, meta, [expr]}, modifiers) do
Expand All @@ -47,6 +49,7 @@ defmodule LiveViewNative.Extensions.InlineRender do
platform_module <- Module.concat(__ENV__.module, context.template_namespace) do
base_opts = [
caller: __CALLER__,
compiled_at: unquote(compiled_at),
engine: Phoenix.LiveView.TagEngine,
file: __CALLER__.file,
indentation: meta[:indentation] || 0,
Expand Down
3 changes: 2 additions & 1 deletion lib/live_view_native/extensions/modifiers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ defmodule LiveViewNative.Extensions.Modifiers do
modifiers_struct: opts[:modifiers_struct],
platform_modifiers: opts[:platform_modifiers],
platform_module: opts[:platform_module]
], location: :keep do
],
location: :keep do
all_modifiers = Keyword.merge(platform_modifiers, custom_modifiers)

if is_nil(platform_module) do
Expand Down
5 changes: 4 additions & 1 deletion lib/live_view_native/extensions/render_macro.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ defmodule LiveViewNative.Extensions.RenderMacro do
"""
defmacro __using__(opts \\ []) do
quote bind_quoted: [
compiled_at: opts[:compiled_at],
render_macro: opts[:render_macro],
platform_id: opts[:platform_id],
role: opts[:role]
], location: :keep do
],
location: :keep do
defmacro unquote(:"#{render_macro}")({:<<>>, meta, [expr]}, _modifiers) do
unless Macro.Env.has_var?(__CALLER__, {:assigns, nil}) do
raise "#{unquote(render_macro)} requires a variable named \"assigns\" to exist and be set to a map"
Expand All @@ -22,6 +24,7 @@ defmodule LiveViewNative.Extensions.RenderMacro do
%LiveViewNativePlatform.Env{} = context <- Map.get(platforms, unquote(platform_id)),
platform_module <- Module.concat(__ENV__.module, context.template_namespace) do
base_opts = [
compiled_at: unquote(compiled_at),
caller: __CALLER__,
engine: Phoenix.LiveView.TagEngine,
file: __CALLER__.file,
Expand Down
20 changes: 5 additions & 15 deletions lib/live_view_native/extensions/stylesheets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,12 @@ defmodule LiveViewNative.Extensions.Stylesheets do

quote bind_quoted: [module: module], location: :keep do
def __compiled_stylesheet__(stylesheet_key) do
class_tree_module =
Module.safe_concat([LiveViewNative, Internal, ClassTree, unquote(module)])
stylesheet_modules = __stylesheet_modules__()

class_tree = apply(class_tree_module, :class_tree, [stylesheet_key])

class_names =
class_tree
|> Map.values()
|> List.flatten()

__stylesheet_modules__()
|> Enum.reduce(%{}, fn stylesheet_module, acc ->
compiled_stylesheet = apply(stylesheet_module, :compile_ast, [class_names])

Map.merge(acc, compiled_stylesheet)
end)
unquote(module)
|> LiveViewNative.Stylesheets.get_class_tree_module()
|> LiveViewNative.Stylesheets.get_class_tree(stylesheet_key)
|> LiveViewNative.Stylesheets.reduce_stylesheets(stylesheet_modules)
|> inspect(limit: :infinity, charlists: :as_list, printable_limit: :infinity)
end

Expand Down
3 changes: 2 additions & 1 deletion lib/live_view_native/extensions/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ defmodule LiveViewNative.Extensions.Templates do
template_basename: opts[:template_basename],
template_directory: opts[:template_directory],
template_extension: opts[:template_extension]
], location: :keep do
],
location: :keep do
template_path = Path.join(template_directory, template_basename) <> template_extension

if is_binary(template_path) and File.exists?(template_path) do
Expand Down
74 changes: 63 additions & 11 deletions lib/live_view_native/layouts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule LiveViewNative.Layouts do
|> extract_layouts_recursive(opts)
|> List.flatten()
|> Enum.map(fn layout_params -> {layout_params.render_function, layout_params} end)
|> Enum.reject(&(format_excluded?(&1, opts)))
|> Enum.reject(&format_excluded?(&1, opts))
|> Enum.into(%{})
|> apply_default_layouts(opts)
|> generate_class_trees(opts)
Expand All @@ -26,7 +26,8 @@ defmodule LiveViewNative.Layouts do
def extract_layouts_recursive({_func, _meta, [_ | _] = nodes}, %{} = opts),
do: Enum.map(nodes, &extract_layouts_recursive(&1, opts))

def extract_layouts_recursive([do: {:__block__, [], args}], %{} = opts), do: extract_layouts_recursive(args, opts)
def extract_layouts_recursive([do: {:__block__, [], args}], %{} = opts),
do: extract_layouts_recursive(args, opts)

def extract_layouts_recursive([_ | _] = nodes, %{} = opts),
do: Enum.map(nodes, &extract_layouts_recursive(&1, opts))
Expand All @@ -49,9 +50,11 @@ defmodule LiveViewNative.Layouts do
|> String.replace(".", "_")
|> String.to_atom()

is_root_template? = "#{func_name}" == "root_#{platform.platform_id}"

%{
class_tree: %{},
template: File.read!(template_path),
template: layout_template(template_path, is_root_template?),
eex_engine: platform.eex_engine,
platform_id: platform.platform_id,
render_function: func_name,
Expand All @@ -62,6 +65,31 @@ defmodule LiveViewNative.Layouts do

def compile_layout(_platform, _template_path, _opts), do: nil

def layout_template(template_path, is_root_template?) do
template_path
|> File.read!()
|> layout_template_with_live_reload(Mix.env())
|> layout_template_with_csrf_token(is_root_template?)
end

def layout_template_with_live_reload(template, :dev) do
"""
#{template}
<iframe src="/phoenix/live_reload/frame" />
"""
end

def layout_template_with_live_reload(template, _mix_env), do: template

def layout_template_with_csrf_token(template, true) do
"""
#{template}
<csrf-token value={get_csrf_token()} />
"""
end

def layout_template_with_csrf_token(template, _is_root_template?), do: template

def matches_template?({_key, %{} = platform}, filename) do
case platform.template_extension do
nil ->
Expand All @@ -73,12 +101,15 @@ defmodule LiveViewNative.Layouts do
end

def generate_class_trees(%{} = layouts, %{} = opts) do
Enum.reduce(layouts, layouts, fn {func_name, %{template: template, platform_id: platform_id} = layout}, acc ->
Enum.reduce(layouts, layouts, fn {func_name,
%{template: template, platform_id: platform_id} = layout},
acc ->
opts = Map.put(opts, :render_function, {layout.render_function, 1})

case LiveViewNative.Templates.compile_class_tree(template, platform_id, opts) do
{:ok, %{} = class_tree} ->
Map.put(acc, func_name, %{layout | class_tree: class_tree})
updated_layout = Map.put(layout, :class_tree, class_tree)
Map.put(acc, func_name, updated_layout)

_ ->
acc
Expand All @@ -88,8 +119,8 @@ defmodule LiveViewNative.Layouts do

def persist_class_trees(%{} = layouts, opts) do
layouts
|> Enum.map(fn {func_name, %{class_tree: class_tree}} -> {func_name, class_tree} end)
|> LiveViewNative.Templates.persist_class_tree_map(opts.caller.module)
|> Enum.map(&extract_class_tree/1)
|> LiveViewNative.Templates.persist_class_tree_map(opts.caller)

layouts
end
Expand All @@ -98,7 +129,7 @@ defmodule LiveViewNative.Layouts do

defp apply_default_layouts(%{} = layouts, %{default_layouts: true, platforms: platforms} = opts) do
platforms
|> Enum.reject(&(format_excluded?(&1, opts)))
|> Enum.reject(&format_excluded?(&1, opts))
|> Enum.flat_map(fn {format, %{default_layouts: %{} = default_layouts} = platform} ->
Enum.map(default_layouts, fn {layout_name, layout_source} ->
{String.to_atom("#{layout_name}_#{format}"), {layout_source, platform}}
Expand All @@ -123,6 +154,16 @@ defmodule LiveViewNative.Layouts do

defp apply_default_layouts(%{} = layouts, _opts), do: layouts

defp extract_class_tree({func_name, layout}) do
case layout do
%{class_tree: class_tree} ->
{func_name, class_tree}

_ ->
{func_name, %{}}
end
end

defp format_excluded?({_, %{platform_id: platform_id}}, %{} = opts) do
case opts do
%{exclude: [_ | _] = excluded_formats} ->
Expand All @@ -134,7 +175,10 @@ defmodule LiveViewNative.Layouts do
end

defmacro __using__(_opts \\ []) do
quote bind_quoted: [caller: Macro.escape(__CALLER__)], location: :keep do
compiled_at = :os.system_time(:nanosecond)

quote bind_quoted: [caller: Macro.escape(__CALLER__), compiled_at: compiled_at],
location: :keep do
use LiveViewNative.Extensions, role: :layouts

layout_templates =
Expand All @@ -154,15 +198,23 @@ defmodule LiveViewNative.Layouts do

eex_opts = [
caller: caller,
compiled_at: compiled_at,
engine: layout_params.eex_engine,
file: __ENV__.file,
render_function: {render_func, 1},
source: layout_params.template,
persist_class_tree: false,
tag_handler: layout_params.tag_handler
]
LiveViewNative.Templates.compile_class_tree(layout_params.template, layout_params.platform_id, eex_opts)
expr = LiveViewNative.Templates.with_stylesheet_wrapper(layout_params.template, render_func)

LiveViewNative.Templates.compile_class_tree(
layout_params.template,
layout_params.platform_id,
eex_opts
)

expr =
LiveViewNative.Templates.with_stylesheet_wrapper(layout_params.template, render_func)

EEx.function_from_string(:def, render_func, expr, [:assigns], eex_opts)
end)
Expand Down
12 changes: 8 additions & 4 deletions lib/live_view_native/live_session.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule LiveViewNative.LiveSession do
case get_native_assigns(socket, params) do
%Assigns{} = native_assigns ->
assigns = Map.from_struct(native_assigns)

socket =
socket
|> assign(assigns)
Expand Down Expand Up @@ -79,10 +80,13 @@ defmodule LiveViewNative.LiveSession do
defp put_target(assigns, _lvn_params), do: assigns

defp put_native_layout(%Socket{} = socket) do
with %Socket{assigns: %{format: format}, private: private, view: view} when not is_nil(view) <- socket,
%{layout: {layout_mod, layout_name}} <- apply(view, :__live__, [])
do
%Socket{socket | private: Map.put(private, :live_layout, {layout_mod, "#{layout_name}_#{format}"})}
with %Socket{assigns: %{format: format}, private: private, view: view} when not is_nil(view) <-
socket,
%{layout: {layout_mod, layout_name}} <- apply(view, :__live__, []) do
%Socket{
socket
| private: Map.put(private, :live_layout, {layout_mod, "#{layout_name}_#{format}"})
}
else
_ ->
socket
Expand Down
20 changes: 11 additions & 9 deletions lib/live_view_native/session_plug.ex
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
defmodule LiveViewNative.SessionPlug do
def init(default), do: default

def call(%Plug.Conn{
params: %{"_lvn" => %{"format" => lvn_platform}},
private:
%{
:phoenix_format => "html",
:phoenix_root_layout => %{"html" => {root_layout_mod, root_layout_func}}
}
} = conn, _default) do
def call(
%Plug.Conn{
params: %{"_lvn" => %{"format" => lvn_platform}},
private: %{
:phoenix_format => "html",
:phoenix_root_layout => %{"html" => {root_layout_mod, root_layout_func}}
}
} = conn,
_default
) do
root_layout_func = String.to_existing_atom("#{root_layout_func}_#{lvn_platform}")
root_layout = {root_layout_mod, root_layout_func}

Expand All @@ -18,4 +20,4 @@ defmodule LiveViewNative.SessionPlug do
end

def call(conn, _default), do: conn
end
end
Loading

0 comments on commit 3fe5e43

Please sign in to comment.