defmodule FlintUI.API.Classes do
@moduledoc """
CSS class helpers.
"""
@doc """
Builds a class string, concatenates a class string, out of a list of:
* Strings;
* Atoms;
* Keyword lists or maps where the keys are the CSS class and the
values are treated as booleans for conditional inclusion of the class.
All `nil`, `false` and duplicated values are filtered out.
## Examples
iex> cx(["foo", "bar"])
"foo bar"
iex> cx([foo: false, bar: true])
"bar"
iex> cx([[a: true, b: false], [c: false, d: true]])
"a d"
iex> cx(["one", :two])
"one two"
iex> cx(yes: true, no: false)
"yes"
iex> cx(%{"a" => true, b: false})
"a"
iex> cx(t: "I'm truthy", f: nil)
"t"
iex> cx([[[nil, false, "hello", :world]]])
"hello world"
"""
def cx(classes) do
[classes]
|> flatten_classes()
|> Enum.uniq()
|> Enum.join(" ")
|> String.trim()
|> case do
"" -> nil
classes -> classes
end
end
defguardp is_class(c) when is_binary(c) or is_atom(c)
# Return a list of classes in string or atom form.
defp classes(nil), do: []
defp classes(false), do: []
defp classes(s) when is_class(s), do: [to_string(s)]
defp classes({k, false}) when is_class(k), do: []
defp classes({k, nil}) when is_class(k), do: []
defp classes({k, _}) when is_class(k), do: [to_string(k)]
defp classes(%{} = map), do: Enum.flat_map(map, &classes/1)
defp classes(_), do: []
defp flatten_classes(class_list) do
class_list
|> List.flatten()
|> Enum.flat_map(&classes/1)
|> Enum.filter(&(!!&1 && &1 != ""))
end
end