3.9 KiB · text History 9c0fc16
defmodule LambentExWeb.MultiSelectComponent do
  # PhoenixFilesWeb.MultiSelectComponent
  use LambentExWeb, :live_component

  def render(assigns) do
    ~H"""
    <div class="flex justify-center">
      <div
        class="border border-gray-200 dark:border-gray-700 w-96 h-12 pb-2 m-2 w-96 flex relative"
        id={"selected-options-container-#{@id}"}
      >
        <%= for option <- @selected_options do %>
          <div
            id={"option_#{option.label}"}
            class="bg-purple-500 shadow-lg rounded-lg mt-2 ml-1 text-white dark:bg-sky-500 inline-block pl-2 pr-2 text-center"
          >
            <%= option.label %>
          </div>
        <% end %>
        <div class="absolute right-0">
          <svg
            id={"#{@id}-down-icon"}
            xmlns="http://www.w3.org/2000/svg"
            class="h-5 w-5 absolute right-0 top-3"
            viewBox="0 0 20 20"
            fill="currentColor"
            phx-click={
              JS.toggle()
              |> JS.toggle(to: "##{@id}-up-icon")
              |> JS.toggle(to: "#options-container-#{@id}")
            }
          >
            <path
              fill-rule="evenodd"
              d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
              clip-rule="evenodd"
            />
          </svg>
          <svg
            id={"#{@id}-up-icon"}
            xmlns="http://www.w3.org/2000/svg"
            class="h-5 w-5  absolute right-0 top-3 hidden"
            viewBox="0 0 20 20"
            fill="currentColor"
            phx-click={
              JS.toggle()
              |> JS.toggle(to: "##{@id}-down-icon")
              |> JS.toggle(to: "#options-container-#{@id}")
            }
          >
            <path
              fill-rule="evenodd"
              d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z"
              clip-rule="evenodd"
            />
          </svg>
        </div>
      </div>
      <div
        class="hidden w-96 mt-4 p-4 ml-2 mt-16 absolute z-10 bg-stone-50 shadow-2xl rounded-lg"
        id={"options-container-#{@id}"}
      >
        <%= inputs_for @form, :options, fn value -> %>
          <div class="form-check">
            <label class="form-check-label inline-block text-gray-800">
              <%= checkbox(value, :selected,
                phx_change: "checked",
                phx_target: @myself,
                value: value.data.selected,
                class:
                  "form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer"
              ) %>
              <%= label(value, :label, value.data.label, class: "ml-2") %>
              <%= hidden_input(value, :label, value: value.data.label) %>
            </label>
          </div>
        <% end %>
      </div>
    </div>
    """
  end

  def update(params, socket) do
    %{options: options, form: form, selected: selected, id: id} = params

    socket =
      socket
      |> assign(:id, id)
      |> assign(:selected_options, filter_selected_options(options))
      |> assign(:options, options)
      |> assign(:form, form)
      |> assign(:selected, selected)

    {:ok, socket}
  end

  def handle_event("checked", %{"multi_select" => %{"options" => values}}, socket) do
    [{index, %{"selected" => selected?}}] = Map.to_list(values)
    index = String.to_integer(index)
    current_option = Enum.at(socket.assigns.options, index)

    updated_options =
      List.replace_at(socket.assigns.options, index, %{current_option | selected: selected?})

    socket.assigns.selected.(updated_options)

    {:noreply, socket}
  end

  defp filter_selected_options(options) do
    Enum.filter(options, fn opt -> opt.selected == true or opt.selected == "true" end)
  end
end