mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-09 22:44:38 +08:00
292 lines
9.3 KiB
Elixir
292 lines
9.3 KiB
Elixir
defmodule LivebookWeb.SessionLive.InsertButtonsComponent do
|
|
use LivebookWeb, :live_component
|
|
|
|
import LivebookWeb.NotebookComponents
|
|
|
|
defguardp is_many(list) when tl(list) != []
|
|
|
|
def render(assigns) do
|
|
~H"""
|
|
<div
|
|
class="relative top-0.5 m-0 flex justify-center"
|
|
role="toolbar"
|
|
aria-label="insert new"
|
|
data-el-insert-buttons
|
|
>
|
|
<div
|
|
class="absolute inset-0 h-[30px] z-[100] bg-white rounded-lg border-2 border-dashed border-gray-400"
|
|
data-el-insert-drop-area
|
|
data-section-id={@section_id}
|
|
data-cell-id={@cell_id}
|
|
id={"cell-#{@id}-dropzone"}
|
|
phx-hook="Dropzone"
|
|
>
|
|
</div>
|
|
<div class={
|
|
"w-full md:absolute z-10 hover:z-[11] #{if(@persistent, do: "opacity-100", else: "opacity-0")} hover:opacity-100 focus-within:opacity-100 flex space-x-2 justify-center items-center"
|
|
}>
|
|
<.menu id={"cell-#{@id}-insert"} position={:bottom_left} distant>
|
|
<:toggle>
|
|
<.insert_button>
|
|
<div
|
|
class="pr-2"
|
|
phx-click="insert_cell_below"
|
|
phx-value-type="code"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
>
|
|
+ <%= @default_language |> Atom.to_string() |> String.capitalize() %>
|
|
</div>
|
|
<div class="-mr-1 pl-1 flex items-center border-l border-gray-200 group-hover:border-gray-300 group-focus:border-gray-300">
|
|
<.remix_icon icon="arrow-down-s-line" class="text-lg leading-none" />
|
|
</div>
|
|
</.insert_button>
|
|
</:toggle>
|
|
<.menu_item>
|
|
<button
|
|
role="menuitem"
|
|
phx-click="set_default_language"
|
|
phx-value-type="code"
|
|
phx-value-language="elixir"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
>
|
|
<.cell_icon cell_type={:code} language={:elixir} />
|
|
<span>Elixir</span>
|
|
</button>
|
|
</.menu_item>
|
|
<.menu_item>
|
|
<button
|
|
role="menuitem"
|
|
phx-click="set_default_language"
|
|
phx-value-type="code"
|
|
phx-value-language="erlang"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
>
|
|
<.cell_icon cell_type={:code} language={:erlang} />
|
|
<span>Erlang</span>
|
|
</button>
|
|
</.menu_item>
|
|
</.menu>
|
|
<.menu id={"#{@id}-block-menu"} position={:bottom_left}>
|
|
<:toggle>
|
|
<.insert_button>+ Block</.insert_button>
|
|
</:toggle>
|
|
<.menu_item>
|
|
<button
|
|
role="menuitem"
|
|
phx-click="insert_cell_below"
|
|
phx-value-type="markdown"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
>
|
|
<.remix_icon icon="markdown-fill" />
|
|
<span>Markdown</span>
|
|
</button>
|
|
</.menu_item>
|
|
<.menu_item>
|
|
<button
|
|
role="menuitem"
|
|
phx-click="insert_section_below"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
>
|
|
<.remix_icon icon="h-2" />
|
|
<span>Section</span>
|
|
</button>
|
|
</.menu_item>
|
|
<.menu_item>
|
|
<button
|
|
role="menuitem"
|
|
phx-click="insert_branching_section_below"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
>
|
|
<.remix_icon icon="git-branch-line" />
|
|
<span>Branching section</span>
|
|
</button>
|
|
</.menu_item>
|
|
<div class="flex items-center mt-4 mb-1 px-5 text-xs text-gray-400 font-light">
|
|
MARKDOWN
|
|
</div>
|
|
<.menu_item>
|
|
<button
|
|
role="menuitem"
|
|
phx-click="insert_cell_below"
|
|
phx-value-type="diagram"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
>
|
|
<.remix_icon icon="organization-chart" />
|
|
<span>Diagram</span>
|
|
</button>
|
|
</.menu_item>
|
|
<.menu_item>
|
|
<button
|
|
phx-click="insert_image"
|
|
phx-value-section_id={@section_id}
|
|
phx-value-cell_id={@cell_id}
|
|
aria-label="insert image"
|
|
role="menuitem"
|
|
>
|
|
<.remix_icon icon="image-add-line" />
|
|
<span>Image</span>
|
|
</button>
|
|
</.menu_item>
|
|
<%= if @example_snippet_definitions != [] do %>
|
|
<div class="flex items-center mt-4 mb-1 px-5 text-xs text-gray-400 font-light">
|
|
CODE
|
|
</div>
|
|
<.menu_item :for={definition <- @example_snippet_definitions}>
|
|
<.example_snippet_insert_button
|
|
definition={definition}
|
|
section_id={@section_id}
|
|
cell_id={@cell_id}
|
|
/>
|
|
</.menu_item>
|
|
<% end %>
|
|
</.menu>
|
|
<%= cond do %>
|
|
<% @runtime_status == :disconnected -> %>
|
|
<.insert_button phx-click={
|
|
JS.push("setup_runtime",
|
|
value: %{reason: "To see the available smart cells, you need a connected runtime."}
|
|
)
|
|
}>
|
|
+ Smart
|
|
</.insert_button>
|
|
<% @smart_cell_definitions == [] -> %>
|
|
<span class="tooltip right" data-tooltip="No smart cells available">
|
|
<.insert_button disabled>+ Smart</.insert_button>
|
|
</span>
|
|
<% true -> %>
|
|
<.menu id={"#{@id}-smart-menu"} position={:bottom_left}>
|
|
<:toggle>
|
|
<.insert_button>+ Smart</.insert_button>
|
|
</:toggle>
|
|
<.menu_item :for={definition <- @smart_cell_definitions}>
|
|
<.smart_cell_insert_button
|
|
definition={definition}
|
|
section_id={@section_id}
|
|
cell_id={@cell_id}
|
|
/>
|
|
</.menu_item>
|
|
</.menu>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
"""
|
|
end
|
|
|
|
attr :disabled, :boolean, default: false
|
|
attr :rest, :global
|
|
|
|
slot :inner_block
|
|
|
|
def insert_button(assigns) do
|
|
~H"""
|
|
<button
|
|
{@rest}
|
|
class={[
|
|
"inline-flex items-center px-2 py-1 rounded-lg font-medium text-sm whitespace-nowrap border",
|
|
if @disabled do
|
|
"cursor-default pointer-events-none border-transparent bg-gray-100 text-gray-400"
|
|
else
|
|
"bg-gray-50 border-gray-200 text-gray-600 hover:bg-gray-100 focus:bg-gray-100"
|
|
end
|
|
]}
|
|
>
|
|
<%= render_slot(@inner_block) %>
|
|
</button>
|
|
"""
|
|
end
|
|
|
|
defp example_snippet_insert_button(assigns) when is_many(assigns.definition.variants) do
|
|
~H"""
|
|
<.submenu>
|
|
<:primary>
|
|
<button role="menuitem">
|
|
<.remix_icon icon={@definition.icon} />
|
|
<span><%= @definition.name %></span>
|
|
</button>
|
|
</:primary>
|
|
<.menu_item :for={{variant, idx} <- Enum.with_index(@definition.variants)}>
|
|
<button
|
|
role="menuitem"
|
|
phx-click={on_example_snippet_click(@definition, idx, @section_id, @cell_id)}
|
|
>
|
|
<span><%= variant.name %></span>
|
|
</button>
|
|
</.menu_item>
|
|
</.submenu>
|
|
"""
|
|
end
|
|
|
|
defp example_snippet_insert_button(assigns) do
|
|
~H"""
|
|
<button
|
|
role="menuitem"
|
|
phx-click={on_example_snippet_click(@definition, 0, @section_id, @cell_id)}
|
|
>
|
|
<.remix_icon icon={@definition.icon} />
|
|
<span><%= @definition.name %></span>
|
|
</button>
|
|
"""
|
|
end
|
|
|
|
defp smart_cell_insert_button(assigns) when is_many(assigns.definition.requirement_presets) do
|
|
~H"""
|
|
<.submenu>
|
|
<:primary>
|
|
<button role="menuitem">
|
|
<span><%= @definition.name %></span>
|
|
</button>
|
|
</:primary>
|
|
<.menu_item :for={{preset, idx} <- Enum.with_index(@definition.requirement_presets)}>
|
|
<button
|
|
role="menuitem"
|
|
phx-click={on_smart_cell_click(@definition, idx, @section_id, @cell_id)}
|
|
>
|
|
<span><%= preset.name %></span>
|
|
</button>
|
|
</.menu_item>
|
|
</.submenu>
|
|
"""
|
|
end
|
|
|
|
defp smart_cell_insert_button(assigns) do
|
|
~H"""
|
|
<button role="menuitem" phx-click={on_smart_cell_click(@definition, @section_id, @cell_id)}>
|
|
<span><%= @definition.name %></span>
|
|
</button>
|
|
"""
|
|
end
|
|
|
|
defp on_example_snippet_click(definition, variant_idx, section_id, cell_id) do
|
|
JS.push("insert_example_snippet_below",
|
|
value: %{
|
|
definition_name: definition.name,
|
|
variant_idx: variant_idx,
|
|
section_id: section_id,
|
|
cell_id: cell_id
|
|
}
|
|
)
|
|
end
|
|
|
|
defp on_smart_cell_click(definition, section_id, cell_id) do
|
|
preset_idx = if definition.requirement_presets == [], do: nil, else: 0
|
|
on_smart_cell_click(definition, preset_idx, section_id, cell_id)
|
|
end
|
|
|
|
defp on_smart_cell_click(definition, preset_idx, section_id, cell_id) do
|
|
JS.push("insert_smart_cell_below",
|
|
value: %{
|
|
kind: definition.kind,
|
|
section_id: section_id,
|
|
cell_id: cell_id,
|
|
preset_idx: preset_idx
|
|
}
|
|
)
|
|
end
|
|
end
|