diff --git a/CHANGELOG.md b/CHANGELOG.md index 515944a3..20a1ae55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- User can add additional classes to table elements + ## v0.6.5 (2024-06-18) - Add `:query_modifier` option to allow dynamic altering of queries - Update Elixir and Erlang versions @@ -15,7 +17,7 @@ - Dependency updates and test fixes only ## v0.6.2 (2023-04-06) -- Rerelease to fix versioning issue +- Re-release to fix versioning issue ## v0.6.1 (2023-04-06) - Fix issue with adding action buttons to table @@ -31,7 +33,7 @@ ## v0.5.5 (2022-07-23) -- Bugfix for default formatter. Previously, it would attempt to_string on all values passing through which breaks `:safe` tuples. +- Bug fix for default formatter. Previously, it would attempt to_string on all values passing through which breaks `:safe` tuples. - Support live routes - Increase test coverage - Fix CodeCov badge link diff --git a/README.md b/README.md index 6c1af5c0..de0caa95 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ Note that if you are navigating to the live table using [Phoenix LiveView live_s - `text: Exzeitable.Text.Default` The translation that appears on the table, defaults to English. - `assigns: %{}` Passes additional assigns to socket.assigns. Keep your payload small! - `query_modifier: {MyModule, :my_function}` Passes the query to MyModule.my_function/2, where query can then be dynamically altered before being returned. Arguments are the query, and the `Exzeitable.Params` struct, which is how Exzeitable stores state. Return value is the query. +- `html_classes` Allows the user to add additional HTML classes to elements. Is a list of 2-element tuples where the first element is a string representing the default class (or classes) and the second element is a string representing the classes to be added. I.e. if the default class is `exz-pagination-ul` then you can append `p-5` to it by adding `html_classes: [{"exz-pagination-ul", "p-5"}]`. This can make styling the table with tailwind or bootstrap much easier. ```elixir defmodule MyApp.MyModule do diff --git a/lib/exzeitable/html.ex b/lib/exzeitable/html.ex index e09a1ab6..809636ab 100644 --- a/lib/exzeitable/html.ex +++ b/lib/exzeitable/html.ex @@ -24,10 +24,13 @@ defmodule Exzeitable.HTML do top_navigation = div_wrap( [ - div_wrap([top_pagination, new_button, show_hide_fields], "exz-pagination-wrapper"), + div_wrap( + [top_pagination, new_button, show_hide_fields], + class(params, "exz-pagination-wrapper") + ), search_box ], - "exz-row" + class(params, "exz-row") ) content_tag( @@ -40,11 +43,24 @@ defmodule Exzeitable.HTML do bottom_buttons, bottom_pagination ], - class: "outer-wrapper", + class: class(params, "outer-wrapper"), onclick: "" ) end + @doc "Appends additional classes onto the default class if user defined them in config" + @spec class(Params.t(), String.t()) :: String.t() + def class(%Params{html_classes: nil}, class) do + class + end + + def class(%Params{html_classes: html_classes}, class) do + case Enum.find(html_classes, &(elem(&1, 0) === class)) do + {_, additional_classes} -> "#{class} #{additional_classes}" + nil -> class + end + end + defp div_wrap(content, class \\ "") do content_tag(:div, content, class: class) end diff --git a/lib/exzeitable/html/action_button.ex b/lib/exzeitable/html/action_button.ex index a79211ec..1f783137 100644 --- a/lib/exzeitable/html/action_button.ex +++ b/lib/exzeitable/html/action_button.ex @@ -1,4 +1,6 @@ defmodule Exzeitable.HTML.ActionButton do + alias Exzeitable.HTML + @moduledoc """ For the actions buttons such as :new, :edit etc, as well as custom buttons. @@ -148,19 +150,19 @@ defmodule Exzeitable.HTML.ActionButton do defp html(route, :new, %Params{} = params) do params |> Text.text(:new) - |> Link.link(to: route, class: "exz-action-new") + |> Link.link(to: route, class: HTML.class(params, "exz-action-new")) end defp html(route, :show, %Params{} = params) do params |> Text.text(:show) - |> Link.link(to: route, class: "exz-action-show") + |> Link.link(to: route, class: HTML.class(params, "exz-action-show")) end defp html(route, :edit, %Params{} = params) do params |> Text.text(:edit) - |> Link.link(to: route, class: "exz-action-edit") + |> Link.link(to: route, class: HTML.class(params, "exz-action-edit")) end defp html(route, :delete, %Params{csrf_token: csrf_token} = params) do @@ -168,7 +170,7 @@ defmodule Exzeitable.HTML.ActionButton do |> Text.text(:delete) |> Link.link( to: route, - class: "exz-action-delete", + class: HTML.class(params, "exz-action-delete"), method: :delete, "data-confirm": Text.text(params, :confirm_action), csrf_token: csrf_token diff --git a/lib/exzeitable/html/pagination.ex b/lib/exzeitable/html/pagination.ex index 58a9ca93..918a2b52 100644 --- a/lib/exzeitable/html/pagination.ex +++ b/lib/exzeitable/html/pagination.ex @@ -4,7 +4,7 @@ defmodule Exzeitable.HTML.Pagination do """ alias Exzeitable.HTML.Helpers - alias Exzeitable.{Params, Text} + alias Exzeitable.{HTML, Params, Text} @type name :: :next | :previous | :dots | pos_integer @type page :: pos_integer @@ -19,8 +19,8 @@ defmodule Exzeitable.HTML.Pagination do next_button = paginate_button(params, :next, page, pages) ([previous_button] ++ numbered_buttons ++ [next_button]) - |> Helpers.tag(:ul, class: "exz-pagination-ul") - |> Helpers.tag(:nav, class: "exz-pagination-nav") + |> Helpers.tag(:ul, class: HTML.class(params, "exz-pagination-ul")) + |> Helpers.tag(:nav, class: HTML.class(params, "exz-pagination-nav")) end # Handle the case where there is only a single page, just gives us some disabled buttons @@ -45,60 +45,63 @@ defmodule Exzeitable.HTML.Pagination do defp paginate_button(%Params{} = params, :next, page, page) do params |> Text.text(:next) - |> Helpers.tag(:a, class: "exz-pagination-a", tabindex: "-1") - |> Helpers.tag(:li, class: "exz-pagination-li-disabled") + |> Helpers.tag(:a, class: HTML.class(params, "exz-pagination-a"), tabindex: "-1") + |> Helpers.tag(:li, class: HTML.class(params, "exz-pagination-li-disabled")) end defp paginate_button(%Params{} = params, :previous, 1, _pages) do params |> Text.text(:previous) - |> Helpers.tag(:a, class: "exz-pagination-a", tabindex: "-1") - |> Helpers.tag(:li, class: "exz-pagination-li-disabled") + |> Helpers.tag(:a, class: HTML.class(params, "exz-pagination-a"), tabindex: "-1") + |> Helpers.tag(:li, class: HTML.class(params, "exz-pagination-li-disabled")) end - defp paginate_button(_params, :dots, _page, _pages) do + defp paginate_button(params, :dots, _page, _pages) do "...." - |> Helpers.tag(:a, class: "exz-pagination-a exz-pagination-width", tabindex: "-1") - |> Helpers.tag(:li, class: "exz-pagination-li-disabled") + |> Helpers.tag(:a, + class: HTML.class(params, "exz-pagination-a exz-pagination-width"), + tabindex: "-1" + ) + |> Helpers.tag(:li, class: HTML.class(params, "exz-pagination-li-disabled")) end defp paginate_button(%Params{} = params, :next, page, _pages) do params |> Text.text(:next) |> Helpers.tag(:a, - class: "exz-pagination-a", + class: HTML.class(params, "exz-pagination-a"), style: "cursor: pointer", "phx-click": "change_page", "phx-value-page": page + 1 ) - |> Helpers.tag(:li, class: "exz-pagination-li") + |> Helpers.tag(:li, class: HTML.class(params, "exz-pagination-li")) end defp paginate_button(%Params{} = params, :previous, page, _pages) do params |> Text.text(:previous) |> Helpers.tag(:a, - class: "exz-pagination-a", + class: HTML.class(params, "exz-pagination-a"), style: "cursor: pointer", "phx-click": "change_page", "phx-value-page": page - 1 ) - |> Helpers.tag(:li, class: "exz-pagination-li") + |> Helpers.tag(:li, class: HTML.class(params, "exz-pagination-li")) end - defp paginate_button(_params, page, page, _pages) when is_integer(page) do - Helpers.tag(page, :a, class: "exz-pagination-a exz-pagination-width") - |> Helpers.tag(:li, class: "exz-pagination-li-active") + defp paginate_button(params, page, page, _pages) when is_integer(page) do + Helpers.tag(page, :a, class: HTML.class(params, "exz-pagination-a exz-pagination-width")) + |> Helpers.tag(:li, class: HTML.class(params, "exz-pagination-li-active")) end - defp paginate_button(_params, page, _page, _pages) when is_integer(page) do + defp paginate_button(params, page, _page, _pages) when is_integer(page) do Helpers.tag(page, :a, - class: "exz-pagination-a exz-pagination-width", + class: HTML.class(params, "exz-pagination-a exz-pagination-width"), style: "cursor: pointer", "phx-click": "change_page", "phx-value-page": page ) - |> Helpers.tag(:li, class: "exz-pagination-li") + |> Helpers.tag(:li, class: HTML.class(params, "exz-pagination-li")) end @doc "Selects the page buttons we need for pagination" diff --git a/lib/exzeitable/html/search.ex b/lib/exzeitable/html/search.ex index a858cc3b..81042e9a 100644 --- a/lib/exzeitable/html/search.ex +++ b/lib/exzeitable/html/search.ex @@ -2,7 +2,7 @@ defmodule Exzeitable.HTML.Search do @moduledoc "Build the search field part of the HTML" alias Exzeitable.HTML.Helpers - alias Exzeitable.{Params, Text} + alias Exzeitable.{HTML, Params, Text} alias PhoenixHTMLHelpers.Form @doc "Returns the HTML search form" @@ -15,31 +15,31 @@ defmodule Exzeitable.HTML.Search do # onkeypress to disable enter key in search field [ phx_change: :search, - class: "exz-search-form", + class: HTML.class(params, "exz-search-form"), onkeypress: "return event.keyCode != 13;" ], fn f -> [ Form.text_input(f, :search, placeholder: Text.text(params, :search), - class: "exz-search-field", + class: HTML.class(params, "exz-search-field"), phx_debounce: debounce ), counter(params) ] - |> Helpers.tag(:div, class: "exz-search-field-wrapper") + |> Helpers.tag(:div, class: HTML.class(params, "exz-search-field-wrapper")) end ) - |> Helpers.tag(:div, class: "exz-search-wrapper") + |> Helpers.tag(:div, class: HTML.class(params, "exz-search-wrapper")) else {:safe, [""]} end end - defp counter(%Params{count: count}) do + defp counter(%Params{count: count} = params) do count - |> Helpers.tag(:span, class: "exz-counter-field") - |> Helpers.tag(:div, class: "exz-counter-field-wrapper") + |> Helpers.tag(:span, class: HTML.class(params, "exz-counter-field")) + |> Helpers.tag(:div, class: HTML.class(params, "exz-counter-field-wrapper")) end # Returns true if any of the fields have search enabled diff --git a/lib/exzeitable/html/show_button.ex b/lib/exzeitable/html/show_button.ex index 83882232..ff5f7989 100644 --- a/lib/exzeitable/html/show_button.ex +++ b/lib/exzeitable/html/show_button.ex @@ -1,7 +1,7 @@ defmodule Exzeitable.HTML.ShowButton do @moduledoc "Show buttons and the buttons that toggle their visibility" alias Exzeitable.HTML.{Filter, Format, Helpers} - alias Exzeitable.{Params, Text} + alias Exzeitable.{HTML, Params, Text} @doc "Returns HTML for all the show column buttons that should be visible" @spec show_buttons(Params.t()) :: [{:safe, iolist}] @@ -22,7 +22,7 @@ defmodule Exzeitable.HTML.ShowButton do params |> Text.text(:show_field, name) |> Helpers.tag(:a, - class: "exz-show-button", + class: HTML.class(params, "exz-show-button"), "phx-click": "show_column", "phx-value-column": key ) @@ -38,7 +38,7 @@ defmodule Exzeitable.HTML.ShowButton do params |> Text.text(:hide_field_buttons) |> Helpers.tag(:a, - class: "exz-info-button", + class: HTML.class(params, "exz-info-button"), "phx-click": "hide_buttons" ) end @@ -47,7 +47,7 @@ defmodule Exzeitable.HTML.ShowButton do params |> Text.text(:show_field_buttons) |> Helpers.tag(:a, - class: "exz-info-button", + class: HTML.class(params, "exz-info-button"), "phx-click": "show_buttons" ) end diff --git a/lib/exzeitable/html/table.ex b/lib/exzeitable/html/table.ex index 77826e5e..ff3d4768 100644 --- a/lib/exzeitable/html/table.ex +++ b/lib/exzeitable/html/table.ex @@ -1,6 +1,6 @@ defmodule Exzeitable.HTML.Table do @moduledoc "Builds the table part of the HTML" - alias Exzeitable.{Params, Text} + alias Exzeitable.{HTML, Params, Text} alias Exzeitable.HTML.{ActionButton, Filter, Format, Helpers} alias PhoenixHTMLHelpers.Link @@ -20,9 +20,9 @@ defmodule Exzeitable.HTML.Table do |> Helpers.tag(:tbody, []) [head, body] - |> Helpers.tag(:table, class: "exz-table") + |> Helpers.tag(:table, class: HTML.class(params, "exz-table")) |> maybe_nothing_found(params) - |> Helpers.tag(:div, class: "exz-table-wrapper") + |> Helpers.tag(:div, class: HTML.class(params, "exz-table-wrapper")) end @spec add_actions_header(keyword, map) :: keyword @@ -71,7 +71,7 @@ defmodule Exzeitable.HTML.Table do params |> Text.text(:hide) |> Helpers.tag(:a, - class: "exz-hide-link", + class: HTML.class(params, "exz-hide-link"), "phx-click": "hide_column", "phx-value-column": key ) @@ -93,7 +93,7 @@ defmodule Exzeitable.HTML.Table do Link.link(label, to: "", - class: "exz-sort-link", + class: HTML.class(params, "exz-sort-link"), "phx-click": "sort_column", "phx-value-column": key ) @@ -103,7 +103,7 @@ defmodule Exzeitable.HTML.Table do nothing_found = params |> Text.text(:nothing_found) - |> Helpers.tag(:div, class: "exz-nothing-found") + |> Helpers.tag(:div, class: HTML.class(params, "exz-nothing-found")) [content, nothing_found] end diff --git a/lib/exzeitable/params.ex b/lib/exzeitable/params.ex index aec33787..3f5019c9 100644 --- a/lib/exzeitable/params.ex +++ b/lib/exzeitable/params.ex @@ -23,6 +23,7 @@ defmodule Exzeitable.Params do csrf_token: String.t(), # optional order: [{:asc | :desc, column}] | nil, + html_classes: Keyword.t(String.t()) | nil, parent: struct | nil, belongs_to: atom | nil, page: pos_integer, @@ -55,6 +56,7 @@ defmodule Exzeitable.Params do :order, :parent, :belongs_to, + :html_classes, page: 1, count: 0, list: [],