2021-03-04 05:56:28 +08:00
|
|
|
defmodule LivebookWeb.Helpers do
|
2021-07-07 20:32:49 +08:00
|
|
|
use Phoenix.Component
|
|
|
|
|
2021-04-22 05:02:09 +08:00
|
|
|
alias LivebookWeb.Router.Helpers, as: Routes
|
2021-02-11 19:42:17 +08:00
|
|
|
|
|
|
|
@doc """
|
2021-03-04 05:56:28 +08:00
|
|
|
Renders a component inside the `Livebook.ModalComponent` component.
|
2021-02-11 19:42:17 +08:00
|
|
|
|
|
|
|
The rendered modal receives a `:return_to` option to properly update
|
|
|
|
the URL when the modal is closed.
|
|
|
|
"""
|
2021-06-03 19:53:03 +08:00
|
|
|
def live_modal(component, opts) do
|
2021-02-11 19:42:17 +08:00
|
|
|
path = Keyword.fetch!(opts, :return_to)
|
2021-04-28 20:28:28 +08:00
|
|
|
modal_class = Keyword.get(opts, :modal_class)
|
|
|
|
|
|
|
|
modal_opts = [
|
2021-07-07 20:32:49 +08:00
|
|
|
id: "modal",
|
2021-04-28 20:28:28 +08:00
|
|
|
return_to: path,
|
|
|
|
modal_class: modal_class,
|
|
|
|
component: component,
|
|
|
|
opts: opts
|
|
|
|
]
|
|
|
|
|
2021-06-03 19:53:03 +08:00
|
|
|
live_component(LivebookWeb.ModalComponent, modal_opts)
|
2021-02-11 19:42:17 +08:00
|
|
|
end
|
2021-02-18 20:14:09 +08:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
Determines user platform based on the given *User-Agent* header.
|
|
|
|
"""
|
2021-04-15 14:56:20 +08:00
|
|
|
@spec platform_from_user_agent(String.t()) :: :linux | :mac | :windows | :other
|
2021-02-18 20:14:09 +08:00
|
|
|
def platform_from_user_agent(user_agent) when is_binary(user_agent) do
|
|
|
|
cond do
|
|
|
|
linux?(user_agent) -> :linux
|
|
|
|
mac?(user_agent) -> :mac
|
|
|
|
windows?(user_agent) -> :windows
|
|
|
|
true -> :other
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp linux?(user_agent), do: String.match?(user_agent, ~r/Linux/)
|
|
|
|
defp mac?(user_agent), do: String.match?(user_agent, ~r/Mac OS X/)
|
|
|
|
defp windows?(user_agent), do: String.match?(user_agent, ~r/Windows/)
|
2021-02-23 05:08:02 +08:00
|
|
|
|
2021-04-05 03:22:28 +08:00
|
|
|
defdelegate ansi_string_to_html(string, opts \\ []), to: LivebookWeb.ANSI
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Converts a string with ANSI escape codes into HTML lines.
|
|
|
|
|
|
|
|
This method is similar to `ansi_string_to_html/2`,
|
|
|
|
but makes sure each line is itself a valid HTML
|
|
|
|
(as opposed to just splitting HTML into lines).
|
|
|
|
"""
|
|
|
|
@spec ansi_to_html_lines(String.t()) :: list(Phoenix.HTML.safe())
|
|
|
|
def ansi_to_html_lines(string) do
|
|
|
|
string
|
|
|
|
|> ansi_string_to_html(
|
|
|
|
# Make sure every line is styled separately,
|
|
|
|
# so that later we can safely split the whole HTML
|
|
|
|
# into valid HTML lines.
|
|
|
|
renderer: fn style, content ->
|
|
|
|
content
|
|
|
|
|> IO.iodata_to_binary()
|
|
|
|
|> String.split("\n")
|
|
|
|
|> Enum.map(&LivebookWeb.ANSI.default_renderer(style, &1))
|
|
|
|
|> Enum.intersperse("\n")
|
|
|
|
end
|
|
|
|
)
|
|
|
|
|> Phoenix.HTML.safe_to_string()
|
|
|
|
|> String.split("\n")
|
|
|
|
|> Enum.map(&Phoenix.HTML.raw/1)
|
|
|
|
end
|
2021-04-22 05:02:09 +08:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
Returns path to specific process dialog within LiveDashboard.
|
|
|
|
"""
|
|
|
|
def live_dashboard_process_path(socket, pid) do
|
2021-07-07 20:32:49 +08:00
|
|
|
pid_str = Phoenix.LiveDashboard.PageBuilder.encode_pid(pid)
|
2021-04-22 05:02:09 +08:00
|
|
|
Routes.live_dashboard_path(socket, :page, node(), "processes", info: pid_str)
|
|
|
|
end
|
2021-05-24 00:22:55 +08:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
Converts human-readable strings to strings which can be used
|
|
|
|
as HTML element IDs (compatible with HTML5)
|
|
|
|
|
|
|
|
At the same time duplicate IDs are enumerated to avoid duplicates
|
|
|
|
"""
|
|
|
|
@spec names_to_html_ids(list(String.t())) :: list(String.t())
|
|
|
|
def names_to_html_ids(names) do
|
|
|
|
names
|
|
|
|
|> Enum.map(&name_to_html_id/1)
|
|
|
|
|> Enum.map_reduce(%{}, fn html_id, counts ->
|
|
|
|
counts = Map.update(counts, html_id, 1, &(&1 + 1))
|
|
|
|
|
|
|
|
case counts[html_id] do
|
|
|
|
1 -> {html_id, counts}
|
|
|
|
count -> {"#{html_id}-#{count}", counts}
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|> elem(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp name_to_html_id(name) do
|
|
|
|
name
|
|
|
|
|> String.trim()
|
|
|
|
|> String.downcase()
|
|
|
|
|> String.replace(~r/\s+/u, "-")
|
|
|
|
end
|
2021-06-08 18:33:50 +08:00
|
|
|
|
|
|
|
@doc """
|
2021-07-07 20:32:49 +08:00
|
|
|
Renders [Remix](https://remixicon.com) icon.
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
<.remix_icon icon="cpu-line" />
|
|
|
|
|
|
|
|
<.remix_icon icon="cpu-line" class="align-middle mr-1" />
|
2021-06-08 18:33:50 +08:00
|
|
|
"""
|
2021-07-07 20:32:49 +08:00
|
|
|
def remix_icon(assigns) do
|
|
|
|
assigns =
|
|
|
|
assigns
|
|
|
|
|> assign_new(:class, fn -> "" end)
|
|
|
|
|> assign(:attrs, assigns_to_attributes(assigns, [:icon, :class]))
|
|
|
|
|
|
|
|
~H"""
|
|
|
|
<i class={"ri-#{@icon} #{@class}"} {@attrs}></i>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Renders a list of select input options with the given one selected.
|
|
|
|
|
|
|
|
## Examples
|
2021-06-08 18:33:50 +08:00
|
|
|
|
2021-07-07 20:32:49 +08:00
|
|
|
<.select
|
|
|
|
name="language"
|
|
|
|
selected={@language}
|
|
|
|
options={[en: "English", pl: "Polski", fr: "Français"]} />
|
|
|
|
"""
|
|
|
|
def select(assigns) do
|
|
|
|
~H"""
|
|
|
|
<select class="input" name={@name}>
|
|
|
|
<%= for {value, label} <- @options do %>
|
|
|
|
<option value={value} selected={value == @selected}>
|
2021-06-20 21:33:10 +08:00
|
|
|
<%= label %>
|
|
|
|
</option>
|
|
|
|
<% end %>
|
|
|
|
</select>
|
2021-06-08 18:33:50 +08:00
|
|
|
"""
|
|
|
|
end
|
2021-06-24 17:58:13 +08:00
|
|
|
|
|
|
|
@doc """
|
|
|
|
Renders a checkbox input styled as a switch.
|
2021-07-07 20:32:49 +08:00
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
<.switch_checkbox
|
|
|
|
name="likes_cats"
|
|
|
|
label="I very much like cats"
|
|
|
|
checked={@likes_cats} />
|
2021-06-24 17:58:13 +08:00
|
|
|
"""
|
2021-07-07 20:32:49 +08:00
|
|
|
def switch_checkbox(assigns) do
|
|
|
|
assigns = assign_new(assigns, :disabled, fn -> false end)
|
|
|
|
|
|
|
|
~H"""
|
2021-06-24 17:58:13 +08:00
|
|
|
<div class="flex space-x-3 items-center justify-between">
|
|
|
|
<span class="text-gray-700"><%= @label %></span>
|
2021-07-07 20:32:49 +08:00
|
|
|
<label class={"switch-button #{if(@disabled, do: "switch-button--disabled")}"}>
|
|
|
|
<input class="switch-button__checkbox" type="checkbox" name={@name} checked={@checked} />
|
2021-06-24 17:58:13 +08:00
|
|
|
<div class="switch-button__bg"></div>
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
2021-07-07 20:32:49 +08:00
|
|
|
|
|
|
|
@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
|
|
|
|
</.choice_button>
|
|
|
|
"""
|
|
|
|
def choice_button(assigns) do
|
|
|
|
assigns =
|
|
|
|
assigns
|
|
|
|
|> assign_new(:class, fn -> "" end)
|
|
|
|
|> assign(:attrs, assigns_to_attributes(assigns, [:active, :class]))
|
|
|
|
|
|
|
|
~H"""
|
|
|
|
<button class={"choice-button #{if(@active, do: "active")} #{@class}"} {@attrs}>
|
|
|
|
<%= render_block(@inner_block) %>
|
|
|
|
</button>
|
|
|
|
"""
|
|
|
|
end
|
2021-02-03 20:13:56 +08:00
|
|
|
end
|