cblavier / phx_component_helpers

@@ -2,18 +2,24 @@
Loading
2 2
  @moduledoc false
3 3
4 4
  @doc false
5 -
  def do_extend_class(assigns, default_classes, class_attribute_name) when is_map(assigns) do
5 +
  def do_css_extend_class(assigns, default_classes, class_attribute_name) when is_map(assigns) do
6 6
    input_class = Map.get(assigns, class_attribute_name) || ""
7 -
    do_extend_class(input_class, default_classes)
7 +
    do_extend_class(assigns, input_class, default_classes)
8 8
  end
9 9
10 10
  @doc false
11 -
  def do_extend_class(options, default_classes, class_attribute_name) when is_list(options) do
11 +
  def do_css_extend_class(options, default_classes, class_attribute_name) when is_list(options) do
12 12
    input_class = Keyword.get(options, class_attribute_name) || ""
13 -
    do_extend_class(input_class, default_classes)
13 +
    do_extend_class(options, input_class, default_classes)
14 14
  end
15 15
16 -
  defp do_extend_class(input_class, default_classes) do
16 +
  defp do_extend_class(assigns_or_options, input_class, default_classes) do
17 +
    default_classes =
18 +
      case default_classes do
19 +
        _ when is_function(default_classes) -> default_classes.(assigns_or_options)
20 +
        _ -> default_classes
21 +
      end
22 +
17 23
    default_classes = String.split(default_classes, [" ", "\n"], trim: true)
18 24
    extend_classes = String.split(input_class, [" ", "\n"], trim: true)
19 25

@@ -21,7 +21,7 @@
Loading
21 21
  end
22 22
  """
23 23
  def extend_form_class(options, default_classes) do
24 -
    extended_classes = do_extend_class(options, default_classes, :class)
24 +
    extended_classes = do_css_extend_class(options, default_classes, :class)
25 25
    Keyword.put(options, :class, extended_classes)
26 26
  end
27 27
end

@@ -134,14 +134,22 @@
Loading
134 134
  end
135 135
136 136
  @doc ~S"""
137 -
  Extends assigns with class attributes.
137 +
  Set assigns with class attributes.
138 138
139 139
  The class attribute will take provided `default_classes` as a default value and will
140 140
  be extended, on a class-by-class basis, by your assigns.
141 141
142 +
  This function will identify default classes to be replaced by assigns on a prefix basis:
143 +
  - "bg-gray-200" will be overwritten by "bg-blue-500" because they share the same "bg-" prefix
144 +
  - "hover:bg-gray-200" will be overwritten by "hover:bg-blue-500" because they share the same
145 +
  "hover:bg-" prefix
146 +
  - "m-1" would not be overwritten by "mt-1" because they don't share the same prefix ("m-" vs "mt-")
147 +
142 148
  ## Parameters
143 149
  * `assigns` - your component assigns
144 150
  * `default_classes` - the default classes that will be overridden by your assigns.
151 +
  This parameter can be a binary or a single parameter function that receives all assigns and
152 +
  returns a binary
145 153
146 154
  ## Options
147 155
  * `:attribute` - read & write css classes from & into this key
@@ -154,6 +162,10 @@
Loading
154 162
  |> extend_class("bg-blue-500 mt-8")
155 163
  |> extend_class("py-4 px-2 divide-y-8 divide-gray-200", attribute: :wrapper_class)
156 164
  |> extend_class("form-input", error_class: "form-input-error", attribute: :input_class)
165 +
  |> extend_class(fn assigns ->
166 +
      default = "p-2 m-4 text-sm "
167 +
      if assigns[:active], do: default <> "bg-indigo-500", else: default <> "bg-gray-200"
168 +
     end)
157 169
  ```
158 170
159 171
  `assigns` now contains `@raw_class` and `@raw_wrapper_class`.
@@ -168,7 +180,7 @@
Loading
168 180
    new_class =
169 181
      assigns
170 182
      |> handle_error_class_option(opts[:error_class], class_attribute_name)
171 -
      |> do_extend_class(default_classes, class_attribute_name)
183 +
      |> do_css_extend_class(default_classes, class_attribute_name)
172 184
173 185
    assigns
174 186
    |> Map.put(:"#{class_attribute_name}", new_class)
@@ -216,6 +228,8 @@
Loading
216 228
217 229
  @doc ~S"""
218 230
  Forward and filter assigns to sub components.
231 +
  By default it doesn't forward anything unless you provide it with any combination
232 +
  of the options described below.
219 233
220 234
  ## Parameters
221 235
@@ -224,6 +238,7 @@
Loading
224 238
  ## Options
225 239
  * `prefix` - will only forward assigns prefixed by the given prefix. Forwarded assign key will no longer have the prefix
226 240
  * `take`- is a list of key (without prefix) that will be picked from assigns to be forwarded
241 +
  * `merge`- takes a map that will be merged as-is to the output assigns
227 242
228 243
  If both options are given at the same time, the resulting assigns will be the union of the two.
229 244
@@ -235,20 +250,10 @@
Loading
235 250
  ```
236 251
  """
237 252
  def forward_assigns(assigns, opts) do
238 -
    cond do
239 -
      opts[:prefix] && opts[:take] ->
240 -
        prefix_assigns = handle_prefix_option(assigns, opts[:prefix])
241 -
        root_assigns = handle_take_option(assigns, opts[:take])
242 -
        Map.merge(prefix_assigns, root_assigns)
243 -
244 -
      opts[:prefix] ->
245 -
        handle_prefix_option(assigns, opts[:prefix])
246 -
247 -
      opts[:take] ->
248 -
        handle_take_option(assigns, opts[:take])
249 -
250 -
      true ->
251 -
        assigns
253 +
    for option <- opts, reduce: %{} do
254 +
      acc ->
255 +
        assigns = handle_forward_option(assigns, option)
256 +
        Map.merge(acc, assigns)
252 257
    end
253 258
  end
254 259

@@ -1,7 +1,7 @@
Loading
1 1
defmodule PhxComponentHelpers.Forward do
2 2
  @moduledoc false
3 3
4 -
  def handle_prefix_option(assigns, prefix) do
4 +
  def handle_forward_option(assigns, {:prefix, prefix}) do
5 5
    prefix = "#{prefix}_"
6 6
7 7
    for {key, val} <- assigns, reduce: %{} do
@@ -17,7 +17,11 @@
Loading
17 17
    end
18 18
  end
19 19
20 -
  def handle_take_option(assigns, attributes) do
20 +
  def handle_forward_option(assigns, {:take, attributes}) do
21 21
    Map.take(assigns, attributes)
22 22
  end
23 +
24 +
  def handle_forward_option(_assigns, {:merge, assigns}) do
25 +
    assigns
26 +
  end
23 27
end
Files Coverage
lib 100.00%
Project Totals (6 files) 100.00%

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading