diff --git a/lib/salad_ui/toggle_group.ex b/lib/salad_ui/toggle_group.ex
new file mode 100644
index 0000000..b929384
--- /dev/null
+++ b/lib/salad_ui/toggle_group.ex
@@ -0,0 +1,152 @@
+defmodule SaladUI.ToggleGroup do
+ @moduledoc false
+ use SaladUI, :component
+
+ @doc """
+ A set of two-state buttons that can be toggled on or off.
+
+
+ ## Example:
+
+ <.toggle_group name="style" type="single" value="bold">
+ <.toggle_group_item value="bold" builder={builder} aria-label="Toggle bold">
+ <.icon name="hero-bold" class="h-4 w-4" />
+
+ <.toggle_group_item value="italic" builder={builder} aria-label="Toggle italic">
+ <.icon name="hero-italic" class="h-4 w-4" />
+
+ <.toggle_group_item value="underline" builder={builder} aria-label="Toggle underline">
+ <.icon name="hero-underline" class="h-4 w-4" />
+
+
+ """
+ attr :name, :any, required: true
+ attr :class, :string, default: nil
+ attr :variant, :string, default: "default"
+ attr :size, :string, default: "default"
+ attr :type, :string, values: ~w(single multiple), default: "single"
+
+ attr :value, :string,
+ default: nil,
+ doc: "The value of the toggle group. It's a single value for single type and a list of values for multiple type."
+
+ attr :disabled, :boolean, default: false
+ attr :rest, :global
+ slot :inner_block
+
+ def toggle_group(assigns) do
+ validate_value_type(assigns)
+
+ ~H"""
+
+ <%= render_slot(@inner_block, assigns) %>
+
+ """
+ end
+
+ defp validate_value_type(%{value: value, type: type} = _assigns) do
+ cond do
+ type == "multiple" and not is_list(value) ->
+ raise ArgumentError, "The value of the toggle group must be a list for multiple type."
+
+ type == "single" and not (is_nil(value) or is_binary(value)) ->
+ raise ArgumentError, "The value of the toggle group must be a single value for single type."
+
+ true ->
+ nil
+ end
+ end
+
+ attr :class, :string, default: nil
+ attr :disabled, :boolean, default: false
+ attr :value, :string, default: nil
+ attr :builder, :map, required: true, doc: "The builder context of toggle group."
+ attr :rest, :global
+ slot :inner_block
+
+ def toggle_group_item(%{builder: %{type: "single"}} = assigns) do
+ assigns =
+ assigns
+ |> assign(:variant_class, variant(assigns.builder))
+ |> assign(:checked, assigns.value == assigns.builder.value)
+
+ ~H"""
+
+ """
+ end
+
+ def toggle_group_item(%{builder: %{type: "multiple"}} = assigns) do
+ assigns =
+ assigns
+ |> assign(:variant_class, variant(assigns.builder))
+ |> assign(:checked, assigns.value in assigns.builder.value)
+
+ ~H"""
+
+ """
+ end
+
+ @variants %{
+ variant: %{
+ "default" => "bg-transparent",
+ "outline" => "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground"
+ },
+ size: %{
+ "default" => "h-10 px-3",
+ "sm" => "h-9 px-2.5",
+ "lg" => "h-11 px-5"
+ }
+ }
+
+ @default_variants %{
+ variant: "default",
+ size: "default"
+ }
+
+ defp variant(props) do
+ variants = Map.take(props, ~w(variant size)a)
+ variants = Map.merge(@default_variants, variants)
+
+ Enum.map_join(variants, " ", fn {key, value} -> @variants[key][value] end)
+ end
+end