diff --git a/lib/salad_ui.ex b/lib/salad_ui.ex index ae00544..fd09b1a 100644 --- a/lib/salad_ui.ex +++ b/lib/salad_ui.ex @@ -21,6 +21,7 @@ defmodule SaladUI do defmacro __using__(_) do quote do + import SaladUI.Helpers import SaladUI.Alert import SaladUI.Avatar import SaladUI.Badge diff --git a/lib/salad_ui/collapsible.ex b/lib/salad_ui/collapsible.ex index ec6380f..57a0933 100644 --- a/lib/salad_ui/collapsible.ex +++ b/lib/salad_ui/collapsible.ex @@ -22,24 +22,23 @@ defmodule SaladUI.Collapsible do required: true, doc: "Id to identify collapsible component, collapsible_trigger uses this id to toggle content visibility" - attr :open, :boolean, default: false, doc: "Initial state of collapsible content" + attr :open, :boolean, default: true, doc: "Initial state of collapsible content" attr :class, :string, default: nil slot(:inner_block, required: true) def collapsible(assigns) do assigns = assigns - |> assign(:builder, %{open: assigns[:open], id: assigns[:id]}) |> assign(:open, normalize_boolean(assigns[:open])) ~H"""
- <%= render_slot(@inner_block, @builder) %> + <%= render_slot(@inner_block) %>
""" end @@ -47,15 +46,16 @@ defmodule SaladUI.Collapsible do @doc """ Render trigger for collapsible component. """ - attr :builder, :map, required: true, doc: "Builder instance for collapsible component" attr(:class, :string, default: nil) + attr :as, :string, default: "button" slot(:inner_block, required: true) def collapsible_trigger(assigns) do ~H""" -
@builder.id)} class={@class}> + <.dynamic_tag name={@as} + onclick={exec_closest("phx-toggle-collapsible", ".collapsible-root")} class={@class}> <%= render_slot(@inner_block) %> -
+ """ end @@ -85,7 +85,7 @@ defmodule SaladUI.Collapsible do @doc """ Show collapsible content. """ - def toggle_collapsible(js \\ %JS{}, %{id: id} = _builder) do + def toggle_collapsible(js \\ %JS{}, id) do JS.toggle(js, to: "##{id} .collapsible-content", in: {"ease-out duration-200", "opacity-0", "opacity-100"}, diff --git a/lib/salad_ui/helpers.ex b/lib/salad_ui/helpers.ex index 958a5b2..fc2ff19 100644 --- a/lib/salad_ui/helpers.ex +++ b/lib/salad_ui/helpers.ex @@ -48,6 +48,16 @@ defmodule SaladUI.Helpers do end end + @doc """ + Normalize id to be used in HTML id attribute + It will replace all non-alphanumeric characters with `-` and downcase the string + """ + def id(id) do + id + |> String.replace(~r/[^a-zA-Z0-9]/, "-") + |> String.downcase() + end + @doc """ Variant helper for generating classes based on side and align """ @@ -197,6 +207,24 @@ defmodule SaladUI.Helpers do Enum.map_join(css_map, "; ", fn {k, v} -> "#{k}: #{v}" end) <> ";" end + @doc """ + This function build js script to invoke JS stored in given attribute. + Similar to JS.exec/2 but this function target the nearest ancestor element. + + ## Examples + + ```heex + + ``` + """ + def exec_closest(attribute, ancestor_selector) do + """ + var el = this.closest("#{ancestor_selector}"); liveSocket.execJS(el, el.getAttribute("#{attribute}")); + """ + end + # Translate error message # borrowed from https://github.com/petalframework/petal_components/blob/main/lib/petal_components/field.ex#L414 defp translate_error({msg, opts}) do diff --git a/lib/salad_ui/sidebar.ex b/lib/salad_ui/sidebar.ex index 97c6f6d..8b6529f 100644 --- a/lib/salad_ui/sidebar.ex +++ b/lib/salad_ui/sidebar.ex @@ -13,28 +13,28 @@ defmodule SaladUI.Sidebar do @sidebar_width_mobile "18rem" @sidebar_width_icon "3rem" - - @doc """ + @doc """ Render - """ + """ attr(:class, :string, default: nil) attr(:rest, :global) slot(:inner_block, required: true) def sidebar_provider(assigns) do assigns = assign(assigns, %{sidebar_width: @sidebar_width, sidebar_width_icon: @sidebar_width_icon}) + ~H"""
@target)} {@rest} > - Close Toggle Sidebar + <%= render_slot(@inner_block) %> + Toggle Sidebar """ end @@ -174,7 +179,7 @@ defmodule SaladUI.Sidebar do data-sidebar="rail" aria-label="Toggle Sidebar" tab-index={-1} - onclick="toggleSidebar() //TODO" + onclick={exec_closest("phx-toggle-sidebar", ".sidebar-root")} title="Toggle Sidebar" class={ classes([ @@ -197,6 +202,7 @@ defmodule SaladUI.Sidebar do """ attr(:class, :string, default: nil) attr(:rest, :global) + slot :inner_block, required: true def sidebar_inset(assigns) do ~H""" @@ -209,7 +215,9 @@ defmodule SaladUI.Sidebar do ]) } {@rest} - /> + > + <%= render_slot(@inner_block) %> + """ end @@ -349,24 +357,28 @@ defmodule SaladUI.Sidebar do TODO: class merge not work well here """ attr(:class, :string, default: nil) + attr :as, :string, default: "div" attr(:rest, :global) slot(:inner_block, required: true) def sidebar_group_label(assigns) do ~H""" -
svg]:size-4 [&>svg]:shrink-0 text-xs", - "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", - @class - ], " ") + Enum.join( + [ + "duration-200 flex h-8 shrink-0 items-center rounded-md px-2 font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0 text-xs", + "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", + @class + ], + " " + ) } {@rest} > <%= render_slot(@inner_block) %> -
+ """ end @@ -406,10 +418,10 @@ defmodule SaladUI.Sidebar do def sidebar_group_content(assigns) do ~H"""
variant_class(@variant_config, input) end + + @doc """ + Toggle sidebar between collapsed and expanded state. + """ + def toggle_sidebar({state1, state2} = _collapsible_states) do + {"data-state", "collapsed", "expanded"} + |> JS.toggle_attribute() + |> JS.toggle_attribute({"data-collapsible", state1, state2}) + end end