Packages
livebook
0.18.1
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_web/components/confirm.ex
defmodule LivebookWeb.Confirm do
use Phoenix.Component
import LivebookWeb.CoreComponents
import LivebookWeb.FormComponents
import Phoenix.LiveView
alias Phoenix.LiveView.JS
@doc """
Shows a confirmation modal.
On confirmation runs `on_confirm`. The function receives a socket
and should return the socket. Note that this socket always comes from
the root LV level, keep this in mind when using in a component and
dealing with assigns.
Make sure to render `confirm_root/1` in the layout.
## 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`
* `: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
* `:options` - a list of togglable options to present alongside the
confirmation message. Each option must be a map:
```
%{
name: String.t(),
label: String.t(),
default: boolean(),
disabled: boolean()
}
```
The option values are passed to the `on_confirm` function as the
second argument
"""
def confirm(socket, on_confirm, opts) do
opts =
Keyword.validate!(
opts,
title: "Are you sure?",
description: nil,
confirm_text: "Yes",
confirm_icon: nil,
danger: true,
opt_out_id: nil,
options: []
)
send(self(), {:confirm, on_confirm, opts})
socket
end
@doc """
Renders the confirmation modal for `confirm/3`.
"""
attr :confirm_state, :map, required: true
def confirm_root(assigns) do
~H"""
<.confirm_modal :if={@confirm_state} id={"confirm-#{@confirm_state.id}"} {@confirm_state.attrs} />
"""
end
defp confirm_modal(assigns) do
~H"""
<.modal id={@id} width="medium" show={true}>
<form
id={"#{@id}-confirm-content"}
class="flex flex-col"
phx-submit={JS.push("confirm") |> hide_modal(@id)}
data-el-confirm-form
>
<h3 class="text-2xl font-semibold text-gray-800">
{@title}
</h3>
<p class="mt-8 text-gray-700">
{@description}
</p>
<div :if={@options != []} class="mt-8">
<h3 class="mb-2 text-lg font-semibold text-gray-800">
Options
</h3>
<div class="flex flex-col gap-2">
<.switch_field
:for={option <- @options}
name={"options[#{option.name}]"}
label={option.label}
value={option.default}
disabled={option.disabled}
/>
</div>
</div>
<div :if={@opt_out_id} class="mt-6">
<.checkbox_field
name="opt_out_id"
value={nil}
checked_value={@opt_out_id}
unchecked_value={nil}
label="Don't show this message again"
small
/>
</div>
<div class="mt-8 flex justify-end">
<div class={["flex gap-2", @danger && "flex-row-reverse"]}>
<.button color="gray" outlined type="button" phx-click={hide_modal(@id)}>
Cancel
</.button>
<.button color={if(@danger, do: "red", else: "blue")} type="submit">
<.remix_icon :if={@confirm_icon} icon={@confirm_icon} />
<span>{@confirm_text}</span>
</.button>
</div>
</div>
</form>
</.modal>
"""
end
def on_mount(:default, _params, _session, socket) do
connect_params = get_connect_params(socket) || %{}
# Opting out is a per-user preference, so we store it on the client
# and send in the connect params
confirm_opt_out_ids = connect_params["confirm_opt_out_ids"] || []
socket =
socket
|> assign(confirm_state: nil, confirm_opt_out_ids: MapSet.new(confirm_opt_out_ids))
|> attach_hook(:confirm, :handle_event, &handle_event/3)
|> attach_hook(:confirm, :handle_info, &handle_info/2)
{:cont, socket}
end
defp handle_event("confirm", params, socket) do
socket =
if opt_out_id = params["opt_out_id"] do
socket
|> update(:confirm_opt_out_ids, &MapSet.put(&1, opt_out_id))
|> push_event("add_confirm_opt_out_id", %{opt_out_id: opt_out_id})
else
socket
end
options =
for {name, value} <- params["options"] || [], into: %{}, do: {name, value == "true"}
on_confirm = socket.assigns.confirm_state.on_confirm
socket =
cond do
is_function(on_confirm, 1) -> on_confirm.(socket)
is_function(on_confirm, 2) -> on_confirm.(socket, options)
end
{:halt, socket}
end
defp handle_event(_event, _params, socket), do: {:cont, socket}
defp handle_info({:confirm, on_confirm, opts}, socket) do
socket =
if opts[:opt_out_id] && opts[:opt_out_id] in socket.assigns.confirm_opt_out_ids do
on_confirm.(socket)
else
assign(socket,
confirm_state: %{
id: Livebook.Utils.random_short_id(),
on_confirm: on_confirm,
attrs: opts
}
)
end
{:cont, socket}
end
defp handle_info(_message, socket), do: {:cont, socket}
end