defmodule FloUI.Grid do
@moduledoc """
DEPRECATED - use SnapFramework layouts instead.
"""
# @moduledoc """
# ## Usage in SnapFramework
# Render this with children passed to it to automatically lay the children out in the grid.
# The children must be given width and height styles for it to work. Inspired by https://github.com/BWheatie/scenic_layout_o_matic
# data is a map in the form of ` elixir %{start_xy: {0, 0}, max_xy: {100, 100}}`
# ``` elixir
# <%= component FloUI.Grid, %{
# start_xy: {0, 0},
# max_xy: {48 * 3, 48}
# },
# translate: {20, 120}
# do %>
# <%= component FloUI.Icon.Button, "Close", id: :icon_button, width: 48, height: 48, translate: {20, 120} do %>
# <%= component FloUI.Icon, {:flo_ui, "icons/clear_white.png"} %>
# <% end %>
# <%= component FloUI.Icon.Button, "Close", id: :icon_button, width: 48, height: 48, translate: {20, 120} do %>
# <%= component FloUI.Icon, {:flo_ui, "icons/clear_white.png"} %>
# <% end %>
# <%= component FloUI.Icon.Button, "Close", id: :icon_button, width: 48, height: 48, translate: {20, 120} do %>
# <%= component FloUI.Icon, {:flo_ui, "icons/clear_white.png"} %>
# <% end %>
# <% end %>
# ```
# """
use SnapFramework.Component,
name: :grid,
template: "lib/layout/grid.eex",
controller: :none,
assigns: [
last_height: 0,
component_xy: {0, 0},
start_xy: {0, 0},
grid_xy: {0, 0},
max_xy: {0, 0}
],
opts: []
defcomponent(:grid, :map)
def setup(%{assigns: %{data: %{start_xy: start_xy, max_xy: max_xy}} = assigns} = scene) do
assigns = %{
assigns
| component_xy: start_xy,
start_xy: start_xy,
grid_xy: start_xy,
max_xy: max_xy
}
{_layout, children} =
Enum.reduce(Enum.with_index(assigns.children), {assigns, []}, fn child, acc ->
do_layout(child, acc)
end)
assign(scene, children: children)
end
def process_info(info, scene) do
send_parent(scene, info)
{:noreply, scene}
end
def process_update(_data, _opts, scene) do
assigns = %{
scene.assigns
| component_xy: scene.assigns.start_xy,
start_xy: scene.assigns.start_xy,
grid_xy: scene.assigns.start_xy,
max_xy: scene.assigns.max_xy
}
{_layout, children} =
Enum.reduce(Enum.with_index(assigns.children), {assigns, []}, fn child, acc ->
do_layout(child, acc)
end)
{:noreply, assign(scene, children: children)}
end
defp do_layout(
{[
type: _,
module: _,
data: _,
opts: _
] = child, i},
{layout, child_list}
)
when is_list(child) do
case translate(child, layout) do
{:error, error} ->
{:error, error}
new_layout ->
translate = new_layout.component_xy
updated_child = [
type: child[:type],
module: child[:module],
data: child[:data],
opts: Keyword.put(child[:opts], :translate, translate)
]
{new_layout, List.insert_at(child_list, i, updated_child)}
# Map.put(new_layout, :children, List.replace_at(new_layout.children, i, updated_child))
end
end
defp do_layout(
{[
type: _,
module: _,
data: _,
opts: _,
children: _
] = child, i},
{layout, child_list}
)
when is_list(child) do
case translate(child, layout) do
{:error, error} ->
{:error, error}
new_layout ->
translate = new_layout.component_xy
updated_child = [
type: child[:type],
module: child[:module],
data: child[:data],
opts: Keyword.put(child[:opts], :translate, translate),
children: child[:children]
]
{new_layout, List.insert_at(child_list, i, updated_child)}
# Map.put(new_layout, :children, List.replace_at(new_layout.children, i, updated_child))
end
end
defp do_layout(
{[
type: _,
module: _,
data: _,
children: _,
opts: _
] = child, i},
{layout, child_list}
)
when is_list(child) do
case translate(child, layout) do
{:error, error} ->
{:error, error}
new_layout ->
translate = new_layout.component_xy
updated_child = [
type: child[:type],
module: child[:module],
data: child[:data],
children: child[:children],
opts: Keyword.put(child[:opts], :translate, translate)
]
{new_layout, List.insert_at(child_list, i, updated_child)}
# Map.put(new_layout, :children, List.replace_at(new_layout.children, i, updated_child))
end
end
defp do_layout({child, _i}, {layout, child_list}) when is_list(child) do
Enum.reduce(Enum.with_index(child), {layout, child_list}, fn nchild, acc ->
do_layout(nchild, acc)
end)
end
defp do_layout(_, layout) do
layout
end
defp translate(
child,
%{
last_height: last_height,
start_xy: start_xy,
grid_xy: grid_xy,
max_xy: max_xy
} = layout
) do
width = child[:opts][:width]
height = child[:opts][:height]
{grid_x, _grid_y} = grid_xy
{start_x, start_y} = start_xy
new_x = start_x + width
case start_xy == max_xy do
true ->
layout
|> Map.put(:start_xy, {start_x, start_y})
|> Map.put(:last_height, height)
false ->
# already in a new group, use start_xy
case fits_in_x?(new_x, max_xy) do
# fits in x
true ->
# fit in y?
case fits_in_y?(start_y, max_xy) do
true ->
# fits
layout
|> Map.put(:start_xy, {new_x, start_y})
|> Map.put(:component_xy, {start_x, start_y})
|> Map.put(:last_height, height)
# Does not fit
false ->
{:error, "Does not fit in grid"}
end
# doesnt fit in x
false ->
# fit in new y?
new_y = start_y + last_height
case fits_in_y?(new_y, max_xy) do
true ->
layout
|> Map.put(:start_xy, {grid_x + width, new_y})
|> Map.put(:component_xy, {grid_x, new_y})
|> Map.put(:last_height, height)
false ->
{:error, "Does not fit in the grid"}
end
end
end
end
defp fits_in_x?(potential_x, {max_x, _}), do: potential_x <= max_x
defp fits_in_y?(potential_y, {_, max_y}), do: potential_y <= max_y
end