Current section
Files
Jump to
Current section
Files
lib/ex_admin/table.ex
Code.ensure_compiled(ExAdmin.Utils)
defmodule ExAdmin.Table do
@moduledoc false
require Integer
use Xain
import ExAdmin.Helpers
import ExAdmin.Utils
import ExAdmin.Render
import ExAdmin.Theme.Helpers
import Kernel, except: [to_string: 1]
alias ExAdmin.Schema
def attributes_table(conn, resource, schema) do
theme_module(conn, Table).theme_attributes_table conn, resource,
schema, model_name(resource)
end
def attributes_table_for(conn, resource, schema) do
theme_module(conn, Table).theme_attributes_table_for conn, resource,
schema, model_name(resource)
end
def do_attributes_table_for(conn, resource, resource_model, schema, table_opts) do
primary_key = Schema.get_id(resource)
div(".panel_contents") do
id = "attributes_table_#{resource_model}_#{primary_key}"
div(".attributes_table.#{resource_model}#{id}") do
table(table_opts) do
tbody do
for field_name <- Map.get(schema, :rows, []) do
build_field(resource, conn, field_name, fn
_contents, {:map, f_name} ->
for {k,v} <- Map.get(resource, f_name) do
tr do
value = ExAdmin.Render.to_string(v)
field_header "#{f_name} #{k}"
td ".td-#{parameterize k} #{value}"
end
end
contents, f_name ->
tr do
field_header field_name
handle_contents(contents, f_name)
end
end)
end
end
end
end
end
end
def field_header({_, %{label: label}}), do: th(humanize label)
def field_header({{_, field_name}, opts}), do: field_header({field_name, opts})
def field_header({_, field_name}) when is_atom(field_name), do: field_header(field_name)
def field_header({field_name, _opts}), do: field_header(field_name)
def field_header(field_name), do: th(humanize field_name)
def panel(conn, schema, _opts \\ []) do
theme_module(conn, Table).theme_panel(conn, schema)
end
defp do_panel_resource(conn, %{__struct__: _} = resource, inx, model_name, columns) do
odd_even = if Integer.is_even(inx), do: "even", else: "odd"
tr_id = if Map.has_key?(resource, :id), do: resource.id, else: inx
tr(".#{odd_even}##{model_name}_#{tr_id}") do
for field <- columns do
case field do
{f_name, fun} when is_function(fun) ->
td ".td-#{parameterize f_name} #{fun.(resource)}"
{f_name, opts} ->
build_field(resource, conn, {f_name, Enum.into(opts, %{})}, fn(contents, f_name) ->
td ".td-#{parameterize f_name} #{contents}"
end)
end
end
end
end
defp do_panel_resource(conn, %{} = resource, inx, model_name, columns) do
odd_even = if Integer.is_even(inx), do: "even", else: "odd"
tr(".#{odd_even}##{model_name}_#{inx}") do
for field <- columns do
case field do
{f_name, fun} when is_function(fun) ->
td ".td-#{parameterize f_name} #{fun.(resource)}"
{f_name, opts} ->
build_field(resource, conn, {f_name, Enum.into(opts, %{})}, fn(contents, f_name) ->
td ".td-#{parameterize f_name} #{contents}"
end)
end
end
end
end
def do_panel(conn, columns \\ [], table_opts \\ [], output \\ [])
def do_panel(_conn, [], _table_opts, output), do: Enum.join(Enum.reverse(output))
def do_panel(conn, [{:table_for, %{resources: resources, columns: columns, opts: opts}} | tail], table_opts, output) do
output = [table(Keyword.merge(table_opts, opts)) do
table_head(columns)
tbody do
model_name = get_resource_model resources
Enum.with_index(resources)
|> Enum.map(fn({resource, inx}) ->
do_panel_resource(conn, resource, inx, model_name, columns)
end)
end
end | output]
do_panel(conn, tail, table_opts, output)
end
def do_panel(conn, [{:contents, %{contents: content}} | tail], table_opts, output) do
output = [
case content do
{:safe, _} -> Phoenix.HTML.safe_to_string(content)
content -> content
end
|> Xain.raw | output]
do_panel(conn, tail, table_opts, output)
end
# skip unknown blocks
def do_panel(conn, [_head|tail], table_opts, output) do
do_panel(conn, tail, table_opts, output)
end
def table_head(columns, opts \\ %{}) do
selectable = Map.get opts, :selectable_column
thead do
tr do
if selectable do
th(".selectable") do
div(".resource_selection_toggle_cell") do
input("#collection_selection_toggle_all.toggle_all", type: "checkbox", name: "collection_selection_toggle_all")
end
end
end
for field <- columns do
build_th field, opts
end
end
end
end
def build_th({field_name, opts}, table_opts) do
build_th(to_string(field_name), opts, table_opts)
end
def build_th(field_name, _),
do: th(".th-#{parameterize field_name} #{humanize field_name}")
def build_th(field_name, opts, %{fields: fields} = table_opts) do
if String.to_atom(field_name) in fields and opts in [%{}, %{link: true}] do
_build_th(field_name, opts, table_opts)
else
th(".th-#{field_name_to_class(field_name)} #{humanize Map.get(opts, :label, to_string(field_name))}")
end
end
def build_th(field_name, _, _), do: build_th(field_name, nil)
def _build_th(field_name, _opts, %{path_prefix: path_prefix, order: {name, sort},
fields: _fields} = table_opts) when field_name == name do
link_order = if sort == "desc", do: "asc", else: "desc"
page_segment = case Map.get table_opts, :page, nil do
nil -> ""
page -> "&page=#{page.page_number}"
end
scope_segment = case table_opts[:scope] do
nil -> ""
scope -> "&scope=#{scope}"
end
th(".sortable.sorted-#{sort}.th-#{field_name}") do
a("#{humanize field_name}", href: path_prefix <>
field_name <> "_#{link_order}#{page_segment}" <> scope_segment <>
Map.get(table_opts, :filter, ""))
end
end
def _build_th(field_name, opts, %{path_prefix: path_prefix} = table_opts) do
sort = Map.get(table_opts, :sort, "asc")
page_segment = case Map.get table_opts, :page, nil do
nil -> ""
page -> "&page=#{page.page_number}"
end
scope_segment = case table_opts[:scope] do
nil -> ""
scope -> "&scope=#{scope}"
end
th(".sortable.th-#{field_name}") do
a("#{humanize Map.get(opts, :label, to_string(field_name))}", href: path_prefix <>
field_name <> "_#{sort}#{page_segment}" <> scope_segment <>
Map.get(table_opts, :filter, ""))
end
end
def handle_contents(%Ecto.DateTime{} = dt, field_name) do
td class: to_class("td-", field_name) do
text to_string(dt)
end
end
def handle_contents(%Ecto.Time{} = dt, field_name) do
td class: to_class("td-", field_name) do
text to_string(dt)
end
end
def handle_contents(%Ecto.Date{} = dt, field_name) do
td class: to_class("td-", field_name) do
text to_string(dt)
end
end
def handle_contents(%{}, _field_name), do: []
def handle_contents(contents, field_name) when is_binary(contents) do
td(to_class(".td-", field_name)) do
text contents
end
end
def handle_contents({:safe, contents}, field_name) do
handle_contents contents, field_name
end
def handle_contents(contents, field_name) do
td(to_class(".td-", field_name), contents)
end
end