Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
mayel committed Oct 8, 2024
1 parent 23bbeaa commit b35cdf1
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 76 deletions.
Empty file.
81 changes: 68 additions & 13 deletions lib/components/core_components.swiftui.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
if Code.ensure_loaded?(LiveViewNative.Component) do

defmodule Bonfire.UI.Common.CoreComponents.SwiftUI do
@moduledoc """
Provides core UI components built for SwiftUI.
Expand All @@ -18,9 +19,62 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
"""

use LiveViewNative.Component
use Bonfire.UI.Common

import LiveViewNative.LiveForm.Component


@doc """
A special component that allows users to inject dynamic live components
"""
attr :module, :atom, required: true
attr :id, :string, required: true
attr :function, :atom, default: :render
slot :default
def stateful_component(assigns) do
# TEMP: until LVN supports live components
stateless_component(with true <- module_enabled?(assigns[:module]),
{:ok, assigns} <- assigns
|> assign(module_default_assigns(assigns[:module]))
|> assign(assigns) # again so we override defaults
|> assign_new(:myself, fn -> nil end)
|> assign_new(:streams, fn -> nil end)
|> apply(assigns[:module], :mount, [...]),
{:ok, assigns} <- apply(assigns[:module], :update, [assigns, assigns]) do
assign_new(assigns, :function, fn -> :render end)
|> debug("assi")
else e ->
error(e)
assigns
|> debug("erssi")
|> assign(:module, __MODULE__)
|> assign(:function, :error_msg)
|> assign(:text, "Could not render component")
end
)
# TODO: when LVN supports live components
# ~LVN"""
# <Phoenix.Component.live_component module={@module} id={@id}><%= render_slot(@default) %></Phoenix.Component.live_component>
# """
end

@doc """
A special component that allows users to inject dynamic function components
"""
attr :module, :atom, default: nil
attr :function, :atom, default: :render
attr :id, :string, default: nil
slot :default
def stateless_component(assigns) do
~LVN"""
<%= Phoenix.LiveView.TagEngine.component(
&apply(@module || __MODULE__, @function || :render, [&1]),
assigns,
{__ENV__.module, __ENV__.function, __ENV__.file, __ENV__.line}
) %>
"""
end

@doc """
Renders an input with label and error messages.
Expand Down Expand Up @@ -124,7 +178,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<%= @label %>
</TextFieldLink>
</LabeledContent>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -135,7 +189,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<DatePicker id={@id} name={@name} selection={@value} {@rest}>
<%= @label %>
</DatePicker>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -147,7 +201,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<Text template="label"><%= @label %></Text>
<MultiDatePicker id={@id} name={@name} selection={@value} {@rest}><%= @label %></MultiDatePicker>
</LabeledContent>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -164,7 +218,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<%= name %>
</Text>
</Picker>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -176,7 +230,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<Text template="label"><%= @label %></Text>
<Slider id={@id} name={@name} value={@value} lowerBound={@min} upperBound={@max} {@rest}><%= @label %></Slider>
</LabeledContent>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -188,7 +242,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<Text template="label"><%= @label %></Text>
<Stepper id={@id} name={@name} value={@value} {@rest}></Stepper>
</LabeledContent>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -200,7 +254,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<Text template="label"><%= @label %></Text>
<TextEditor id={@id} name={@name} text={@value} {@rest} />
</LabeledContent>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -209,7 +263,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
~LVN"""
<VStack alignment="leading">
<TextField id={@id} name={@name} text={@value} prompt={@prompt} {@rest}><%= @placeholder || @label %></TextField>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -218,7 +272,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
~LVN"""
<VStack alignment="leading">
<SecureField id={@id} name={@name} text={@value} prompt={@prompt} {@rest}><%= @placeholder || @label %></SecureField>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -230,7 +284,7 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
<Text template="label"><%= @label %></Text>
<Toggle id={@id} name={@name} isOn={Map.get(assigns, :checked, Map.get(assigns, :value))} {@rest}></Toggle>
</LabeledContent>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error_msg :for={msg <- @errors}><%= msg %></.error_msg>
</VStack>
"""
end
Expand All @@ -239,12 +293,13 @@ if Code.ensure_loaded?(LiveViewNative.Component) do
Generates a generic error message.
"""
@doc type: :component
slot :inner_block, required: true
attr :text, :string, required: false, default: nil
slot :inner_block, required: false

def error(assigns) do
def error_msg(assigns) do
~LVN"""
<Group style="font(.caption); foregroundStyle(.red)">
<%= render_slot(@inner_block) %>
<%= @text %> <%= render_slot(@inner_block) %>
</Group>
"""
end
Expand Down
2 changes: 1 addition & 1 deletion lib/components/nav/header/guest_header_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ defmodule Bonfire.UI.Common.GuestHeaderLive do
prop page, :string, default: "home"

# @decorate time()
render_sface_or_native()
# render_sface_or_native()
end
4 changes: 2 additions & 2 deletions lib/components/nav/user_menu/user_menu_links_live.sface
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@
</LinkLive>
</li> -->

<!-- <li>
{!-- <li>
<StatelessComponent
module={maybe_component(Bonfire.UI.Groups.NewGroupLive, @__context__)}
parent_id="user_menu_links"
open_btn_wrapper_class="flex items-center gap-2 text-sm text-base-content"
open_btn_class=""
/>
</li> -->
</li> --}

<li>
<LinkLive to={~p"/settings"} class="flex items-center gap-2 text-sm text-base-content">
Expand Down
2 changes: 1 addition & 1 deletion lib/endpoint_live_reload.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule Bonfire.UI.Common.Endpoint.LiveReload do
plug(Phoenix.LiveReloader)
plug(Phoenix.CodeReloader)

if unquote(System.get_env("NATIVE_ENABLED") in ["1", "true"]) do
if unquote(System.get_env("WITH_LV_NATIVE") in ["1", "true"]) do
plug LiveViewNative.LiveReloader
end

Expand Down
1 change: 0 additions & 1 deletion lib/layout/app.swiftui.heex

This file was deleted.

2 changes: 2 additions & 0 deletions lib/layout/app.swiftui.neex
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<.flash_group flash={@flash} />
<%= @inner_content %>
1 change: 0 additions & 1 deletion lib/layout/root.swiftui.heex

This file was deleted.

5 changes: 5 additions & 0 deletions lib/layout/root.swiftui.neex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<.csrf_token />
<Style url={~p"/assets/app.swiftui.styles"} />
<NavigationStack>
<%= @inner_content %>
</NavigationStack>
20 changes: 18 additions & 2 deletions lib/static_generator/static_generator_plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,30 @@ defmodule Bonfire.UI.Common.StaticGeneratorPlug do
conn
end

def make_request_path_static(%{query_params: %{"_format" => format}} = conn, _) do
# for LVN
debug("skip cache")
conn
# do_make_request_path_static(conn, format)
end

def make_request_path_static(%{query_params: %{"_email_format" => format}} = conn, _) do
# for email templates
do_make_request_path_static(conn, format)
end

def make_request_path_static(conn, _) do
filename = "index.html"
do_make_request_path_static(conn)
end

defp do_make_request_path_static(conn, ext \\ "html") do
filename = "index.#{ext}"

request_path = conn.request_path || "/"

#  only generate expired or non-existing caches if on demand mode is enabled (vs for example cron mode)
with true <- Config.get([__MODULE__, :generate_mode]) == :on_demand,
%{error: _} <- Bonfire.UI.Common.StaticGenerator.maybe_generate(request_path) do
%{error: _} <- Bonfire.UI.Common.StaticGenerator.maybe_generate(request_path, ext: ext) do
error("Could not find or generate a static cache at #{request_path}")
conn
else
Expand Down
70 changes: 70 additions & 0 deletions lib/themes/default/app.swiftui.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
defmodule Bonfire.UI.Common.Themes.Default.App.SwiftUI do
use LiveViewNative.Stylesheet, :swiftui

# Add your styles here
# Refer to your client's documentation on what the proper syntax
# is for defining rules within classes
~SHEET"""
"""

def class("main_header") do
~RULES"""
toolbar(content: :toolbar)
navigationTitle(:title)
toolbarTitleMenu(content: :content)
navigationBarTitleDisplayMode(.inline)
toolbarBackgroundVisibility(.visible, for: .navigationBar)
toolbarBackground(.ultraThinMaterial, for: .navigationBar)
"""
end

def class("simple_header") do
~RULES"""
toolbar(content: :toolbar)
navigationTitle(:title)
navigationBarTitleDisplayMode(.inline)
"""
end

def class("detents:" <> props) do
[height, size] = String.split(props, ":")

# {height, _} = Integer.parse(height)

~RULES"""
presentationDetents([.{height}, .{size}])
"""
end

def class("dragindicator:" <> props) do

~RULES"""
presentationDragIndicator(.{props})
"""
end


def class("ultrathinmaterial") do
~RULES"""
presentationBackground(.ultraThinMaterial)
"""
end

# If you need to have greater control over how your style rules are created
# you can use the function defintion style which is more verbose but allows
# for more fine-grained controled
#
# This example shows what is not possible within the more concise ~SHEET
# use `<Text class="frame:w100:h200" />` allows for a setting
# of both the `width` and `height` values.

# def class("frame:" <> dims) do
# [width] = Regex.run(~r/w(\d+)/, dims, capture: :all_but_first)
# [height] = Regex.run(~r/h(\d+)/, dims, capture: :all_but_first)

# ~RULES"""
# frame(width: {width}, height: {height})
# """
# end
end
28 changes: 28 additions & 0 deletions lib/ui_common.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,16 @@ defmodule Bonfire.UI.Common do
Inserts one or many items in an existing stream.
See `Phoenix.LiveView.stream_insert/4` for opts.
"""
def maybe_stream_insert(%{assigns: %{streams: streams}} = socket, name, items, _opts) when is_nil(streams) or streams==%{} do
error(
assigns(socket),
"Invalid stream '#{name}' to render data in. Will set as regular assign instead"
)

socket
|> assign_generic(name, items)
end

def maybe_stream_insert(%{assigns: %{streams: _}} = socket, name, items, opts)
when is_list(items) do
Phoenix.LiveView.stream(socket, name, items, opts)
Expand Down Expand Up @@ -1424,4 +1434,22 @@ defmodule Bonfire.UI.Common do
end

def assigns(_), do: %{}

def component_props(module) do
component_attr(module, :prop)
end
def component_data(module) do
component_attr(module, :data)
end
defp component_attr(module, key) do
apply(Bonfire.UI.Social.FeedLive, :__info__, [:attributes])
|> Keyword.get_values(key)
|> Enum.flat_map(&(&1))
end

def module_default_assigns(module) do
for %{name: name, opts: opts} <- component_props(module) ++ component_data(module), Keyword.has_key?(opts, :default) do
{name, opts[:default]}
end
end
end
Loading

0 comments on commit b35cdf1

Please sign in to comment.