defmodule LivebookWeb.CoreComponents do use Phoenix.Component alias Phoenix.LiveView.JS @doc """ Renders [Remix](https://remixicon.com) icon. ## Examples <.remix_icon icon="cpu-line" /> <.remix_icon icon="cpu-line" class="align-middle mr-1" /> """ attr :icon, :string, required: true attr :class, :string, default: nil attr :rest, :global def remix_icon(assigns) do ~H""" """ end @doc """ Renders flash notices. ## Examples <.flash kind={:info} flash={@flash} /> """ attr :flash, :map, default: %{}, doc: "the map of flash messages to display" attr :kind, :atom, values: [:info, :success, :warning, :error], doc: "used for styling and flash lookup" attr :rest, :global, doc: "the arbitrary HTML attributes to add to the flash container" def flash(assigns) do ~H""" """ end @doc """ Shows the flash group with standard titles and content. ## Examples <.flash_group flash={@flash} /> """ attr :flash, :map, required: true, doc: "the map of flash messages" def flash_group(assigns) do ~H"""
<.flash kind={:info} flash={@flash} /> <.flash kind={:success} flash={@flash} /> <.flash kind={:warning} flash={@flash} /> <.flash kind={:error} flash={@flash} />
""" end @doc """ Creates a live region with the given role. ## Examples <.live_region role="alert" /> <.live_region role="status" /> """ def live_region(assigns) do ~H"""
""" end @doc """ Sends a message to be read by the screen reader by changing the text content of the live region """ def sr_message(js \\ %JS{}, message) do JS.dispatch(js, "lb:set_text", to: "#live-region", detail: %{value: message}) end @doc """ Wraps the given content in a modal dialog. ## Example <.modal id="edit-modal" patch={...}> <.live_component module={MyComponent} /> """ attr :id, :string, required: true attr :show, :boolean, default: false attr :patch, :string, default: nil attr :navigate, :string, default: nil attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def modal(assigns) do ~H"""
<.focus_wrap id={"#{@id}-content"} class={["relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl", @class]} role="dialog" aria-modal="true" tabindex="0" autofocus phx-window-keydown={hide_modal(@id)} phx-click-away={hide_modal(@id)} phx-key="escape" > <.link :if={@patch} patch={@patch} class="hidden" id={"#{@id}-return"}> <.link :if={@navigate} patch={@navigate} class="hidden" id={"#{@id}-return"}> <%= render_slot(@inner_block) %>
""" end @doc """ Shows a modal rendered with `modal/1`. """ def show_modal(js \\ %JS{}, id) do js |> JS.show( to: "##{id}", transition: {"ease-out duration-200", "opacity-0", "opacity-100"} ) end @doc """ Hides a modal rendered with `modal/1`. """ def hide_modal(js \\ %JS{}, id) do js |> JS.hide( to: "##{id}", transition: {"ease-in duration-200", "opacity-100", "opacity-0"} ) |> JS.dispatch("click", to: "##{id}-return") end @doc """ Renders the confirmation modal for `with_confirm/3`. """ attr :id, :string, required: true def confirm_modal(assigns) do ~H""" <.modal id={@id} class="w-full max-w-xl" phx-hook="ConfirmModal" data-js-show={show_modal(@id)}>

""" end @doc """ Shows a confirmation modal before executing the given JS action. The modal template must already be on the page, see `confirm_modal/1`. ## Options * `:title` - title of the confirmation modal. Defaults to `"Are you sure?"` * `:description` - content of the confirmation modal. Required * `:confirm_text` - text of the confirm button. Defaults to `"Yes"` * `:confirm_icon` - icon in the confirm button. Optional * `:danger` - whether the action is destructive or regular. Defaults to `true` * `:html` - whether the `:description` is a raw HTML. Defaults to `false` * `:opt_out_id` - enables the "Don't show this message again" checkbox. Once checked by the user, the confirmation with this id is never shown again. Optional ## Examples """ def with_confirm(js \\ %JS{}, on_confirm, opts) do opts = Keyword.validate!( opts, [ :confirm_icon, :description, :opt_out_id, title: "Are you sure?", confirm_text: "Yes", danger: true, html: false ] ) JS.dispatch(js, "lb:confirm_request", detail: %{ on_confirm: Jason.encode!(on_confirm.ops), title: opts[:title], description: Keyword.fetch!(opts, :description), confirm_text: opts[:confirm_text], confirm_icon: opts[:confirm_icon], danger: opts[:danger], html: opts[:html], opt_out_id: opts[:opt_out_id] } ) end @doc """ Renders a popup menu that shows up on toggle click. ## Examples <.menu id="my-menu"> <:toggle> <:content> """ attr :id, :string, required: true attr :disabled, :boolean, default: false, doc: "whether the menu is active" attr :position, :atom, default: :bottom_right, values: [:top_left, :top_right, :bottom_left, :bottom_right] attr :distant, :boolean, default: false, doc: "whether the menu should be further from the anchor element" attr :secondary_click, :boolean, default: false, doc: "whether secondary click (usually right mouse click) should open the menu" slot :toggle, required: true slot :content, required: true def menu(assigns) do ~H""" """ end defp menu_content_class(:top_left), do: "menu__content--top-left" defp menu_content_class(:top_right), do: "menu__content--top-right" defp menu_content_class(:bottom_left), do: "menu__content--bottom-left" defp menu_content_class(:bottom_right), do: "menu__content--bottom-right" @doc """ A menu item that shows a submenu on hover. This component should be used within `menu/1` content. ## Example <.submenu> <:content> """ slot :inner_block, required: true slot :content, required: true def submenu(assigns) do ~H""" """ end @doc """ Renders a text content skeleton. """ attr :empty, :boolean, default: false, doc: "if the source is empty" attr :bg_class, :string, default: "bg-gray-200", doc: "the skeleton background color" def content_skeleton(assigns) do ~H""" <%= if @empty do %>
<% else %>
<% end %> """ end @doc """ Renders a highlighted code snippet. ## Examples <.code_preview source_id="my-snippet" language="elixir" source="System.version()" /> """ attr :source_id, :string, required: true attr :language, :string, required: true attr :source, :string, required: true def code_preview(assigns) do ~H"""
<%= @source %>
""" end @doc """ Renders text with a tiny label. ## Examples <.labeled_text label="Name">Sherlock Holmes """ attr :label, :string, required: true attr :one_line, :boolean, default: false, doc: "whether to force the text into a single scrollable line" slot :inner_block, required: true def labeled_text(assigns) do ~H"""
<%= @label %> <%= render_slot(@inner_block) %>
""" end @doc """ Renders a choice button that is either active or not. ## Examples <.choice_button active={@tab == "my_tab"} phx-click="set_my_tab"> My tab """ attr :active, :boolean, required: true attr :disabled, :boolean attr :class, :string, default: nil attr :rest, :global slot :inner_block, required: true def choice_button(assigns) do assigns = assigns |> assign_new(:disabled, fn -> assigns.active end) ~H""" """ end # JS commands @doc """ Toggles classes on elements. """ def toggle_class(js \\ %JS{}, names, opts \\ []) do opts = Keyword.validate!(opts, [:to]) to = Keyword.fetch!(opts, :to) names |> String.split() |> Enum.reduce(js, fn name, js -> js |> JS.remove_class(name, to: "#{to}.#{name}") |> JS.add_class(name, to: "#{to}:not(.#{name})") end) end end