Packages
flop_phoenix
0.22.2
0.26.1
0.26.0
0.25.3
0.25.2
0.25.1
0.25.0
0.24.1
0.24.0
0.23.1
0.23.0
0.22.10
0.22.9
0.22.8
0.22.7
0.22.6
0.22.5
0.22.4
0.22.3
0.22.2
0.22.1
0.22.0
0.21.2
0.21.1
0.21.0
0.20.0
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.2
0.17.1
0.17.0
0.16.0
0.15.2
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Phoenix components for pagination, sortable tables and filter forms using Flop.
Current section
Files
Jump to
Current section
Files
lib/flop_phoenix/form_data.ex
defimpl Phoenix.HTML.FormData, for: Flop.Meta do
alias Flop.Filter
alias Flop.Meta
alias Flop.Phoenix.Misc
def to_form(meta, opts) do
{name, opts} = name_and_opts(meta, opts)
{hidden, opts} = Keyword.pop(opts, :hidden, [])
id = Keyword.get(opts, :id) || name || "flop"
%Phoenix.HTML.Form{
data: meta.flop,
errors: meta.errors,
hidden: hidden_inputs(meta, hidden),
id: id,
impl: __MODULE__,
name: name,
options: opts,
params: meta.params,
source: meta
}
end
defp name_and_opts(_meta, opts) do
case Keyword.pop(opts, :as) do
{nil, opts} -> {nil, opts}
{name, opts} -> {to_string(name), opts}
end
end
defp hidden_inputs(
%Meta{flop: %Flop{} = flop, params: %{} = params, schema: schema},
hidden
) do
default_limit = Flop.get_option(:default_limit, for: schema)
default_order = Flop.get_option(:default_order, for: schema)
page_size = params["page_size"] || flop.page_size
limit = params["limit"] || flop.limit
first = params["first"] || flop.first
last = params["last"] || flop.last
order_by = params["order_by"] || flop.order_by
order_directions = params["order_directions"] || flop.order_directions
order_params = %{order_by: order_by, order_directions: order_directions}
hidden
|> Misc.maybe_put(:page_size, page_size, default_limit)
|> Misc.maybe_put(:limit, limit, default_limit)
|> Misc.maybe_put(:first, first, default_limit)
|> Misc.maybe_put(:last, last, default_limit)
|> Misc.maybe_put_order_params(order_params, default_order)
end
def to_form(
meta,
%{data: %Flop{} = flop} = form,
:filters,
opts
) do
no_unsupported_options!(opts)
{id, opts} = Keyword.pop(opts, :id)
{name, opts} = Keyword.pop(opts, :as)
{default, opts} = Keyword.pop(opts, :default, [])
{fields, opts} = Keyword.pop(opts, :fields)
{offset, opts} = Keyword.pop(opts, :offset, 0)
{skip_hidden_op, opts} = Keyword.pop(opts, :skip_hidden_op, false)
name = if name = name || form.name, do: name <> "[filters]", else: "filters"
id = if id = id || form.id, do: id <> "_filters", else: "filters"
filters =
if form.errors == [],
do: flop.filters,
else: Map.get(form.params, "filters", [])
filter_errors = Keyword.get(form.errors, :filters, [])
filters_with_errors =
filters
|> filters_for(fields, default, filter_errors)
|> reject_unfilterable(meta.schema)
for {{filter, errors}, index} <-
Enum.with_index(filters_with_errors, offset) do
index_string = Integer.to_string(index)
hidden = get_hidden(filter, skip_hidden_op)
{data, params} =
case filter do
%Filter{} -> {filter, %{}}
%{} -> {%Filter{}, filter}
end
%Phoenix.HTML.Form{
source: meta,
impl: __MODULE__,
index: index,
id: id <> "_" <> index_string,
name: name <> "[" <> index_string <> "]",
data: data,
errors: errors,
params: params,
hidden: hidden,
options: opts
}
end
end
def to_form(_meta, _form, field, _opts) do
raise ArgumentError,
"Only :filters is supported on " <>
"inputs_for with Flop.Meta, got: #{inspect(field)}."
end
# no filters, use default
defp filters_for([], nil, default, _), do: zip_errors(default, [])
# no static field configuration
defp filters_for(filters, nil, _, errors), do: zip_errors(filters, errors)
# with static field configuration
defp filters_for(filters, fields, _, errors) when is_list(fields) do
filters_with_errors = zip_errors(filters, errors)
fields
|> Enum.reduce([], &filter_reducer(&1, &2, filters_with_errors))
|> Enum.reverse()
end
defp get_hidden(filter, false = _skip_hidden_op) do
Misc.maybe_put([field: value(filter, :field)], :op, value(filter, :op), :==)
end
defp get_hidden(filter, true = _skip_hidden_op) do
[field: value(filter, :field)]
end
defp zip_errors(filters, []), do: Enum.map(filters, &{&1, []})
defp zip_errors(filters, errors) when is_list(errors),
do: Enum.zip(filters, errors)
defp filter_reducer({field, opts}, acc, filters)
when is_atom(field) and is_list(opts) do
op = opts[:op] || :==
default = opts[:default]
if filter = find_filter_for_field(field, op, filters) do
[filter | acc]
else
[{%Filter{field: field, op: op, value: default}, []} | acc]
end
end
defp filter_reducer(field, acc, filters) when is_atom(field) do
filter_reducer({field, []}, acc, filters)
end
defp find_filter_for_field(field, op, filters) do
Enum.find(
filters,
fn {filter, _errors} ->
value(filter, :field) in [field, Atom.to_string(field)] &&
value(filter, :op, :==) in [op, Atom.to_string(op)]
end
)
end
defp reject_unfilterable(filters, nil), do: filters
defp reject_unfilterable(filters_with_errors, schema) do
filterable = schema |> struct() |> Flop.Schema.filterable()
filterable = filterable ++ Enum.map(filterable, &Atom.to_string/1)
Enum.reject(filters_with_errors, fn {filter, _} ->
value(filter, :field) not in filterable
end)
end
defp no_unsupported_options!(opts) do
for key <- [:append, :as, :hidden, :prepend] do
if Keyword.has_key?(opts, key) do
raise ArgumentError,
"#{inspect(key)} is not supported on inputs_for with Flop.Meta."
end
end
end
def input_type(_meta, _form, :after), do: :text_input
def input_type(_meta, _form, :before), do: :text_input
def input_type(_meta, _form, :first), do: :number_input
def input_type(_meta, _form, :last), do: :number_input
def input_type(_meta, _form, :limit), do: :number_input
def input_type(_meta, _form, :offset), do: :number_input
def input_type(_meta, _form, :page), do: :number_input
def input_type(_meta, _form, :page_size), do: :number_input
def input_type(
_meta,
%{data: %Filter{field: field}, source: %{schema: schema}},
:value
)
when not is_nil(schema) do
schema |> ecto_type(field) |> input_type_for_ecto_type()
end
def input_type(_meta, _form, _field), do: :text_input
defp input_type_for_ecto_type(:boolean), do: :checkbox
defp input_type_for_ecto_type(:date), do: :date_select
defp input_type_for_ecto_type(:integer), do: :number_input
defp input_type_for_ecto_type(:naive_datetime), do: :datetime_select
defp input_type_for_ecto_type(:naive_datetime_usec), do: :datetime_select
defp input_type_for_ecto_type(:time), do: :time_select
defp input_type_for_ecto_type(:time_usec), do: :time_select
defp input_type_for_ecto_type(:utc_datetime), do: :datetime_select
defp input_type_for_ecto_type(:utc_datetime_usec), do: :datetime_select
defp input_type_for_ecto_type(_), do: :text_input
def input_validations(_meta, _form, :after), do: [maxlength: 100]
def input_validations(_meta, _form, :before), do: [maxlength: 100]
def input_validations(_meta, _form, :offset), do: [min: 0]
def input_validations(_meta, _form, :page), do: [min: 1]
def input_validations(_meta, %{source: %Flop.Meta{schema: nil}}, field)
when field in [:first, :last, :limit, :page_size],
do: [min: 1]
def input_validations(_meta, %{source: %Flop.Meta{schema: schema}}, field)
when field in [:first, :last, :limit, :page_size] do
if max_limit = Flop.get_option(:max_limit, for: schema) do
[min: 1, max: max_limit]
else
[min: 1]
end
end
def input_validations(
_meta,
%{data: %Filter{field: field}, source: %{schema: schema}},
:value
)
when not is_nil(schema) do
case :type |> schema.__schema__(field) |> input_type_for_ecto_type() do
:text_input -> [maxlength: 100]
_ -> []
end
end
def input_validations(_meta, _form, _field), do: []
def input_value(_meta, %{data: data, params: params}, field)
when is_atom(field) do
key = Atom.to_string(field)
case params do
%{^key => value} -> value
%{} -> Map.get(data, field)
end
end
def input_value(_meta, _form, field) do
raise ArgumentError, "expected field to be an atom, got: #{inspect(field)}"
end
defp value(map, key, default \\ nil) when is_atom(key) do
string_key = Atom.to_string(key)
case map do
%{^string_key => value} -> value
%{} -> Map.get(map, key, default)
end
end
defp ecto_type(module, field) do
%Flop.FieldInfo{ecto_type: type} =
module |> struct() |> Flop.Schema.field_info(field)
type
end
end