Encapsulate buttons and inputs in components (#2477)

This commit is contained in:
Jonatan Kłosko 2024-02-08 19:27:16 +01:00 committed by GitHub
parent 8a81fda7ea
commit 4587396c9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 997 additions and 1142 deletions

View file

@ -1,101 +1,20 @@
@layer components {
/* Buttons */
.button-base {
@apply inline-flex items-center px-5 py-2 rounded-lg border border-transparent font-medium text-sm whitespace-nowrap;
}
.button-blue {
@apply border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:bg-blue-700;
}
.button-red {
@apply border-transparent bg-red-600 text-white hover:bg-red-700 focus:bg-red-700;
}
.button-gray {
@apply border-gray-200 bg-gray-100 text-gray-600 hover:bg-gray-200 focus:bg-gray-200;
}
.button-outlined-blue {
@apply bg-blue-50 border-blue-600 text-blue-600 hover:bg-blue-100 focus:bg-blue-100;
}
.button-outlined-red {
@apply bg-red-50 border-red-600 text-red-600 hover:bg-red-100 focus:bg-red-100;
}
.button-outlined-gray {
@apply bg-white border-gray-300 text-gray-600 hover:bg-gray-100 focus:bg-gray-100;
}
.button-base:disabled,
.button-base.disabled {
@apply cursor-default pointer-events-none border-transparent bg-gray-100 text-gray-400;
}
.button-small {
@apply px-2 py-1 bg-gray-50 border-gray-200 text-gray-600 hover:bg-gray-100 focus:bg-gray-100;
}
.button-square-icon {
@apply p-2 flex items-center justify-center;
}
.button-square-icon i {
@apply text-xl leading-none;
}
.icon-button {
@apply p-1 flex items-center justify-center text-gray-500 hover:text-gray-900 rounded-full;
}
.icon-button:focus {
@apply bg-gray-100;
}
.icon-button:disabled,
.icon-button.disabled {
@apply cursor-default pointer-events-none text-gray-300;
}
.icon-button i {
line-height: 1;
}
.icon-outlined-button {
@apply rounded-full border-2;
}
/* Form fields */
.input {
@apply w-full px-3 py-2 bg-gray-50 text-sm border border-gray-200 rounded-lg placeholder-gray-400 text-gray-600 phx-form-error:border-red-300 disabled:opacity-70 disabled:cursor-not-allowed;
}
.input[type="color"] {
@apply py-0 w-16;
}
.input--error {
@apply bg-red-50 border-red-600 text-red-600;
}
.input-range {
.range-input {
height: 8px;
@apply appearance-none bg-gray-200 rounded-lg;
}
.input-range::-webkit-slider-thumb {
width: 20px;
height: 20px;
@apply appearance-none border-transparent bg-blue-600 hover:bg-blue-700 cursor-pointer rounded-xl;
/*
Note that we need separate selectors, because at most one of these
pseudo-selectors is valid in the given browser, and an invalid one
would cause the whole rule to be invalid and ignored
*/
.range-input::-moz-range-thumb {
@apply w-5 h-5 appearance-none border-transparent bg-blue-600 hover:bg-blue-700 cursor-pointer rounded-xl;
}
.input-range::-moz-range-thumb {
width: 20px;
height: 20px;
@apply appearance-none border-transparent bg-blue-600 hover:bg-blue-700 cursor-pointer rounded-xl;
.range-input::-webkit-slider-thumb {
@apply w-5 h-5 appearance-none border-transparent bg-blue-600 hover:bg-blue-700 cursor-pointer rounded-xl;
}
/* Custom scrollbars */

View file

@ -136,9 +136,7 @@ solely client-side operations.
left: calc(-45vw + 50%);
}
[data-el-cell][data-js-amplified]
[data-el-amplify-outputs-button]
.icon-button {
[data-el-cell][data-js-amplified] [data-el-amplify-outputs-button] > button {
@apply bg-gray-100 text-gray-900;
}
@ -180,7 +178,7 @@ solely client-side operations.
[data-el-cell][data-type="smart"][data-js-source-visible]
[data-el-toggle-source-button]
.icon-button {
> button {
@apply bg-gray-100 text-gray-900;
}

View file

@ -125,7 +125,11 @@ if (hasCookiesAccess()) {
third-party cookies.
</div>
<div class="mt-6">
<a id="open-app" class="button-base button-blue" target="_blank">
<a
id="open-app"
class="px-5 py-2 font-medium text-sm inline-flex rounded-lg border whitespace-nowrap items-center justify-center gap-1 border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:bg-blue-700"
target="_blank"
>
Open app
</a>
</div>

View file

@ -205,8 +205,9 @@ defmodule LivebookWeb.AppComponents do
<span class="text-sm text-gray-700 font-semibold">Dockerfile</span>
<div class="grow" />
<%= render_slot(@dockerfile_actions) %>
<button
class="button-base button-gray whitespace-nowrap py-1 px-2"
<.button
color="gray"
small
data-tooltip="Copied to clipboard"
type="button"
aria-label="copy to clipboard"
@ -215,9 +216,9 @@ defmodule LivebookWeb.AppComponents do
|> JS.add_class("", transition: {"tooltip top", "", ""}, time: 2000)
}
>
<.remix_icon icon="clipboard-line" class="align-middle mr-1 text-xs" />
<span class="font-normal text-xs">Copy source</span>
</button>
<.remix_icon icon="clipboard-line" />
<span>Copy source</span>
</.button>
</div>
<.code_preview source_id="dockerfile-source" source={@dockerfile} language="dockerfile" />

View file

@ -120,16 +120,13 @@ defmodule LivebookWeb.Confirm do
</div>
<div class="mt-8 flex justify-end">
<div class={["flex gap-2", @danger && "flex-row-reverse"]}>
<button class="button-base button-outlined-gray" type="button" phx-click={hide_modal(@id)}>
<.button color="gray" outlined type="button" phx-click={hide_modal(@id)}>
Cancel
</button>
<button
class={["button-base", if(@danger, do: "button-red", else: "button-blue")]}
type="submit"
>
<.remix_icon :if={@confirm_icon} icon={@confirm_icon} class="align-middle mr-1" />
</.button>
<.button color={if(@danger, do: "red", else: "blue")} type="submit">
<.remix_icon :if={@confirm_icon} icon={@confirm_icon} />
<span><%= @confirm_text %></span>
</button>
</.button>
</div>
</div>
</form>

View file

@ -696,6 +696,7 @@ defmodule LivebookWeb.CoreComponents do
<:col :let={user} label="id"><%= user.id %></:col>
<:col :let={user} label="username"><%= user.username %></:col>
</.table>
"""
attr :id, :string, required: true
attr :rows, :list, required: true
@ -742,17 +743,15 @@ defmodule LivebookWeb.CoreComponents do
phx-click={@row_click && @row_click.(row)}
class={["relative p-0", @row_click && "hover:cursor-pointer"]}
>
<div class="block p-4 sm:px-6">
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-gray-100 sm:rounded-l-xl" />
<span class="relative">
<%= render_slot(col, @row_item.(row)) %>
</span>
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-gray-100 rounded-l-xl" />
<div class="relative block p-4 sm:px-6">
<%= render_slot(col, @row_item.(row)) %>
</div>
</td>
<td :if={@action != []} class="relative p-0">
<span class="absolute -inset-y-px -right-4 left-0 group-hover:bg-gray-100 rounded-r-xl" />
<div class="relative whitespace-nowrap py-4 pl-3 pr-4 sm:pr-6 flex justify-end items-center">
<span class="absolute -inset-y-px -right-4 left-0 group-hover:bg-gray-100 sm:rounded-r-xl" />
<span :for={action <- @action} class="relative ml-4">
<span :for={action <- @action} class="ml-4">
<%= render_slot(action, @row_item.(row)) %>
</span>
</div>
@ -764,6 +763,134 @@ defmodule LivebookWeb.CoreComponents do
"""
end
@doc ~S"""
Renders a button.
## Examples
<.button>Click</.button>
<.button color="gray" outlined>Click</.button>
<.button color="gray" small>Click</.button>
"""
attr :disabled, :boolean, default: false
attr :color, :string, default: "blue", values: ~w(blue gray red)
attr :outlined, :boolean, default: false
attr :small, :boolean, default: false
attr :class, :string, default: nil
attr :rest, :global, include: ~w(href patch navigate download name)
slot :inner_block
def button(assigns)
when is_map_key(assigns.rest, :href) or is_map_key(assigns.rest, :patch) or
is_map_key(assigns.rest, :navigate) do
~H"""
<.link class={[button_classes(@small, @disabled, @color, @outlined), @class]} {@rest}>
<%= render_slot(@inner_block) %>
</.link>
"""
end
def button(assigns) do
~H"""
<button
class={[button_classes(@small, @disabled, @color, @outlined), @class]}
disabled={@disabled}
{@rest}
>
<%= render_slot(@inner_block) %>
</button>
"""
end
defp button_classes(small, disabled, color, outlined) do
[
if small do
"px-2 py-1 font-normal text-xs"
else
"px-5 py-2 font-medium text-sm"
end,
"inline-flex rounded-lg border whitespace-nowrap items-center justify-center gap-1.5",
if disabled do
"cursor-default pointer-events-none border-transparent bg-gray-100 text-gray-400"
else
case {color, outlined} do
{"blue", false} ->
"border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:bg-blue-700"
{"red", false} ->
"border-transparent bg-red-600 text-white hover:bg-red-700 focus:bg-red-700"
{"gray", false} ->
"border-gray-200 bg-gray-100 text-gray-600 hover:bg-gray-200 focus:bg-gray-200"
{"blue", true} ->
"bg-blue-50 border-blue-600 text-blue-600 hover:bg-blue-100 focus:bg-blue-100"
{"red", true} ->
"bg-red-50 border-red-600 text-red-600 hover:bg-red-100 focus:bg-red-100"
{"gray", true} ->
"bg-transparent border-gray-300 text-gray-600 hover:bg-gray-100 focus:bg-gray-100"
end
end
]
end
@doc ~S"""
Renders an icon button.
## Examples
<.icon_button>
<.remix_icon icon="refresh-line" />
</.icon_button>
"""
attr :disabled, :boolean, default: false
attr :small, :boolean, default: false
attr :class, :string, default: nil
attr :rest, :global, include: ~w(href patch navigate download name)
slot :inner_block
def icon_button(assigns)
when is_map_key(assigns.rest, :href) or is_map_key(assigns.rest, :patch) or
is_map_key(assigns.rest, :navigate) do
~H"""
<.link class={[icon_button_classes(@small, @disabled), @class]} {@rest}>
<%= render_slot(@inner_block) %>
</.link>
"""
end
def icon_button(assigns) do
~H"""
<button class={[icon_button_classes(@small, @disabled), @class]} disabled={@disabled} {@rest}>
<%= render_slot(@inner_block) %>
</button>
"""
end
defp icon_button_classes(small, disabled) do
[
unless small do
"text-xl"
end,
"p-1 flex items-center justify-center rounded-full leading-none",
if disabled do
"cursor-default text-gray-300"
else
"text-gray-500 hover:text-gray-900 focus:bg-gray-100"
end
]
end
# JS commands
@doc """

View file

@ -15,9 +15,10 @@ defmodule LivebookWeb.FormComponents do
attr :errors, :list, default: []
attr :field, Phoenix.HTML.FormField, doc: "a form field struct retrieved from the form"
attr :help, :string, default: nil
attr :type, :string, default: "text"
attr :class, :string, default: nil
attr :rest, :global, include: ~w(autocomplete readonly disabled)
attr :rest, :global, include: ~w(autocomplete readonly disabled step min max)
def text_field(assigns) do
assigns = assigns_from_field(assigns)
@ -25,17 +26,21 @@ defmodule LivebookWeb.FormComponents do
~H"""
<.field_wrapper id={@id} name={@name} label={@label} errors={@errors} help={@help}>
<input
type="text"
type={@type}
name={@name}
id={@id || @name}
value={Phoenix.HTML.Form.normalize_value("text", @value)}
class={["input", @class]}
class={[input_classes(), @class]}
{@rest}
/>
</.field_wrapper>
"""
end
defp input_classes() do
"w-full px-3 py-2 bg-gray-50 text-sm font-normal border border-gray-200 rounded-lg placeholder-gray-400 text-gray-600 disabled:opacity-70 disabled:cursor-not-allowed phx-form-error:bg-red-50 phx-form-error:border-red-600 phx-form-error:text-red-600 invalid:bg-red-50 invalid:border-red-600 invalid:text-red-600"
end
@doc """
Renders a textarea input with label and error messages.
"""
@ -46,8 +51,9 @@ defmodule LivebookWeb.FormComponents do
attr :errors, :list, default: []
attr :field, Phoenix.HTML.FormField, doc: "a form field struct retrieved from the form"
attr :help, :string, default: nil
attr :class, :string, default: nil
attr :resizable, :boolean, default: false
attr :monospace, :boolean, default: false
attr :rest, :global, include: ~w(autocomplete readonly disabled rows cols)
@ -59,7 +65,7 @@ defmodule LivebookWeb.FormComponents do
<textarea
id={@id || @name}
name={@name}
class={["input", not @resizable && "resize-none"]}
class={[input_classes(), "resize-none tiny-scrollbar", @monospace && "font-mono", @class]}
{@rest}
><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea>
</.field_wrapper>
@ -103,16 +109,45 @@ defmodule LivebookWeb.FormComponents do
~H"""
<.field_wrapper id={@id} name={@name} label={@label} errors={@errors} help={@help}>
<.with_password_toggle id={@id <> "-toggle"}>
<div id={"#{@id}-toggle"} class="relative flex">
<input
type="password"
name={@name}
id={@id || @name}
value={Phoenix.HTML.Form.normalize_value("text", @value)}
class={["input pr-8", @class]}
class={[input_classes(), "pr-8", @class]}
{@rest}
/>
</.with_password_toggle>
<div class="flex items-center absolute inset-y-0 right-1">
<.icon_button
data-show
type="button"
aria-label="show password"
tabindex="-1"
phx-click={
JS.set_attribute({"type", "text"}, to: "##{@id}-toggle input")
|> JS.add_class("hidden", to: "##{@id}-toggle [data-show]")
|> JS.remove_class("hidden", to: "##{@id}-toggle [data-hide]")
}
>
<.remix_icon icon="eye-line" />
</.icon_button>
<.icon_button
class="hidden"
data-hide
type="button"
aria-label="hide password"
tabindex="-1"
phx-click={
JS.set_attribute({"type", "password"}, to: "##{@id}-toggle input")
|> JS.remove_class("hidden", to: "##{@id}-toggle [data-show]")
|> JS.add_class("hidden", to: "##{@id}-toggle [data-hide]")
}
>
<.remix_icon icon="eye-off-line" />
</.icon_button>
</div>
</div>
</.field_wrapper>
"""
end
@ -149,14 +184,16 @@ defmodule LivebookWeb.FormComponents do
name={@name}
id={@id || @name}
value={@value}
class="input"
class={input_classes()}
spellcheck="false"
maxlength="7"
{@rest}
/>
<button class="icon-button absolute right-2 top-1" type="button" phx-click={@randomize}>
<.remix_icon icon="refresh-line" class="text-xl" />
</button>
<div class="absolute right-2 top-1">
<.icon_button type="button" phx-click={@randomize}>
<.remix_icon icon="refresh-line" />
</.icon_button>
</div>
</div>
</div>
</.field_wrapper>
@ -528,63 +565,6 @@ defmodule LivebookWeb.FormComponents do
"""
end
@doc """
Renders a wrapper around password input with an added visibility
toggle button.
The toggle switches the input's type between `password` and `text`.
## Examples
<.with_password_toggle id="secret-password-toggle">
<input type="password" ...>
</.with_password_toggle>
"""
attr :id, :string, required: true
slot :inner_block, required: true
def with_password_toggle(assigns) do
~H"""
<div id={@id} class="relative flex">
<%= render_slot(@inner_block) %>
<div class="flex items-center absolute inset-y-0 right-1">
<button
class="icon-button"
data-show
type="button"
aria-label="show password"
tabindex="-1"
phx-click={
JS.remove_attribute("type", to: "##{@id} input")
|> JS.set_attribute({"type", "text"}, to: "##{@id} input")
|> JS.add_class("hidden", to: "##{@id} [data-show]")
|> JS.remove_class("hidden", to: "##{@id} [data-hide]")
}
>
<.remix_icon icon="eye-line" class="text-xl" />
</button>
<button
class="icon-button hidden"
data-hide
type="button"
aria-label="hide password"
tabindex="-1"
phx-click={
JS.remove_attribute("type", to: "##{@id} input")
|> JS.set_attribute({"type", "password"}, to: "##{@id} input")
|> JS.remove_class("hidden", to: "##{@id} [data-show]")
|> JS.add_class("hidden", to: "##{@id} [data-hide]")
}
>
<.remix_icon icon="eye-off-line" class="text-xl" />
</button>
</div>
</div>
"""
end
@doc """
Renders a drag-and-drop area for the given upload.

View file

@ -42,10 +42,9 @@ defmodule LivebookWeb.AppAuthLive do
<div class="text-2xl text-gray-800 w-full pt-2">
<form class="flex flex-col space-y-4 items-center" phx-submit="authenticate">
<div phx-feedback-for="password" class={["w-[20ch]", @errors != [] && "show-errors"]}>
<input
<.text_field
type="password"
name="password"
class="input"
value={@password}
placeholder="Password"
autofocus
@ -57,9 +56,9 @@ defmodule LivebookWeb.AppAuthLive do
<%= translate_error(error) %>
</span>
</div>
<button type="submit" class="button-base button-blue">
<.button type="submit">
Authenticate
</button>
</.button>
</form>
</div>
</div>

View file

@ -62,10 +62,10 @@ defmodule LivebookWeb.AppLive do
<% end %>
</p>
<div class="flex justify-end">
<.link class="button-base button-outlined-blue" patch={~p"/apps/#{@app.slug}/new"}>
<.remix_icon icon="add-line" class="align-middle mr-1" />
<.button outlined patch={~p"/apps/#{@app.slug}/new"}>
<.remix_icon icon="add-line" />
<span>New session</span>
</.link>
</.button>
</div>
<div :if={@app_settings.show_existing_sessions} class="w-full flex flex-col space-y-4">
<.link

View file

@ -172,13 +172,10 @@ defmodule LivebookWeb.AppSessionLive do
</.link>
</span>
<button
class={[
"button-base bg-transparent",
"border-red-400 text-red-400 hover:bg-red-50 focus:bg-red-50"
]}
class="px-5 py-2 font-medium text-sm inline-flex rounded-lg border whitespace-nowrap items-center justify-center gap-1 border-red-400 text-red-400 hover:bg-red-50 focus:bg-red-50"
phx-click="queue_errored_cells_evaluation"
>
<.remix_icon icon="play-circle-fill" class="align-middle mr-1" />
<.remix_icon icon="play-circle-fill" />
<span>Retry</span>
</button>
</div>
@ -201,9 +198,9 @@ defmodule LivebookWeb.AppSessionLive do
'''
}
>
<button phx-click="queue_full_evaluation" class="icon-button">
<.remix_icon icon="play-circle-fill" class="text-3xl" />
</button>
<.icon_button phx-click="queue_full_evaluation">
<.remix_icon icon="play-circle-fill" class="text-3xl leading-none" />
</.icon_button>
</span>
<.app_status_circle status={@data_view.app_status} />
</div>

View file

@ -45,13 +45,12 @@ defmodule LivebookWeb.AppSessionLive.SourceComponent do
</span>
<div class="flex justify-end space-x-2">
<span class="tooltip left" data-tooltip="Copy source">
<button
class="icon-button"
<.icon_button
aria-label="copy source"
phx-click={JS.dispatch("lb:clipcopy", to: "#export-notebook-source")}
>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>
<.remix_icon icon="clipboard-line" />
</.icon_button>
</span>
</div>
</div>

View file

@ -98,13 +98,12 @@ defmodule LivebookWeb.AppsDashboardLive do
</div>
<div class="flex flex-col md:flex-row md:items-center justify-start lg:justify-end">
<span class="tooltip top" data-tooltip="Terminate">
<button
class="icon-button text-right"
<.icon_button
aria-label="terminate app"
phx-click={JS.push("terminate_app", value: %{slug: app.slug})}
>
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
</div>
</div>
@ -135,30 +134,22 @@ defmodule LivebookWeb.AppsDashboardLive do
</:col>
<:actions :let={app_session}>
<span class="tooltip left" data-tooltip="Open">
<a
class={[
"icon-button",
app_session.app_status.lifecycle != :active && "disabled"
]}
<.icon_button
disabled={app_session.app_status.lifecycle}
aria-label="open app"
href={~p"/apps/#{app.slug}/#{app_session.id}"}
>
<.remix_icon icon="link" class="text-lg" />
</a>
<.remix_icon icon="link" />
</.icon_button>
</span>
<span class="tooltip left" data-tooltip="Debug">
<a
class="icon-button"
aria-label="debug app"
href={~p"/sessions/#{app_session.id}"}
>
<.remix_icon icon="terminal-line" class="text-lg" />
</a>
<.icon_button aria-label="debug app" href={~p"/sessions/#{app_session.id}"}>
<.remix_icon icon="terminal-line" />
</.icon_button>
</span>
<%= if app_session.app_status.lifecycle == :active do %>
<span class="tooltip left" data-tooltip="Deactivate">
<button
class="icon-button"
<.icon_button
aria-label="deactivate app session"
phx-click={
JS.push("deactivate_app_session",
@ -166,13 +157,12 @@ defmodule LivebookWeb.AppsDashboardLive do
)
}
>
<.remix_icon icon="stop-circle-line" class="text-lg" />
</button>
<.remix_icon icon="stop-circle-line" />
</.icon_button>
</span>
<% else %>
<span class="tooltip left" data-tooltip="Terminate">
<button
class="icon-button"
<.icon_button
aria-label="terminate app session"
phx-click={
JS.push("terminate_app_session",
@ -180,8 +170,8 @@ defmodule LivebookWeb.AppsDashboardLive do
)
}
>
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
<% end %>
</:actions>

View file

@ -106,12 +106,10 @@ defmodule LivebookWeb.FileSelectComponent do
phx-nosubmit={!@submit_event}
phx-target={@myself}
>
<input
class="input"
<.text_field
id={"#{@id}-input-path"}
aria-label="file path"
phx-hook="FocusOnUpdate"
type="text"
name="path"
placeholder="File"
value={@file.path}
@ -126,9 +124,9 @@ defmodule LivebookWeb.FileSelectComponent do
position={:bottom_right}
>
<:toggle>
<button class="icon-button" tabindex="-1" aria-label="add">
<.remix_icon icon="add-line" class="text-xl" />
</button>
<.icon_button tabindex="-1" aria-label="add">
<.remix_icon icon="add-line" />
</.icon_button>
</:toggle>
<.menu_item>
<button role="menuitem" phx-click={js_show_new_item_section("#{@id}-new-dir-section")}>
@ -215,6 +213,20 @@ defmodule LivebookWeb.FileSelectComponent do
aria-labelledby="import-from-file"
/>
<div
:if={@uploads.folder.entries != []}
class="border-b border-dashed border-grey-200 mb-2 pb-2"
>
<div :for={file <- @uploads.folder.entries} class="p-2 flex gap-2 items-center">
<.spinner />
<span class="font-medium text-gray-500"><%= file.client_name %></span>
<div class="grow" />
<.icon_button type="button" phx-click="clear-file" phx-target={@myself} tabindex="-1">
<.remix_icon icon="close-line" />
</.icon_button>
</div>
</div>
<div
:if={any_highlighted?(@file_infos)}
class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-2 border-b border-dashed border-grey-200 mb-2 pb-2"
@ -240,18 +252,6 @@ defmodule LivebookWeb.FileSelectComponent do
renamed_name={@renamed_name}
/>
<% end %>
<div :for={file <- @uploads.folder.entries} class="flex items-center justify-between">
<span class="font-medium text-gray-400"><%= file.client_name %></span>
<button
type="button"
class="icon-button"
phx-click="clear-file"
phx-target={@myself}
tabindex="-1"
>
<.remix_icon icon="close-line" class="text-xl text-gray-300 hover:text-gray-500" />
</button>
</div>
</div>
</form>
</div>
@ -297,9 +297,10 @@ defmodule LivebookWeb.FileSelectComponent do
~H"""
<.menu id={@id} disabled={@file_system_select_disabled} position={:bottom_left}>
<:toggle>
<button
<.button
color="gray"
type="button"
class="button-base button-gray pl-3 pr-2"
class="pl-3 pr-2"
aria-label="switch file storage"
disabled={@file_system_select_disabled}
>
@ -307,7 +308,7 @@ defmodule LivebookWeb.FileSelectComponent do
<div class="pl-0.5 flex items-center">
<.remix_icon icon="arrow-down-s-line" class="text-lg leading-none" />
</div>
</button>
</.button>
</:toggle>
<%= for file_system <- @file_systems do %>
<%= if file_system.id == @file.file_system_id do %>

View file

@ -45,16 +45,13 @@ defmodule LivebookWeb.HomeLive do
>
<:topbar_action>
<div class="flex space-x-2">
<.link
navigate={~p"/open/storage"}
class="button-base button-outlined-gray whitespace-nowrap"
>
<.button color="gray" outlined navigate={~p"/open/storage"}>
Open
</.link>
<.link class="button-base button-blue" patch={~p"/new"}>
<.remix_icon icon="add-line" class="align-middle mr-1" />
</.button>
<.button color="blue" patch={~p"/new"}>
<.remix_icon icon="add-line" />
<span>New notebook</span>
</.link>
</.button>
</div>
</:topbar_action>
@ -65,16 +62,13 @@ defmodule LivebookWeb.HomeLive do
<div class="flex flex-row space-y-0 items-center pb-4 justify-between">
<LayoutComponents.title text="Home" />
<div class="hidden md:flex space-x-2" role="navigation" aria-label="new notebook">
<.link
navigate={~p"/open/storage"}
class="button-base button-outlined-gray whitespace-nowrap"
>
<.button color="gray" outlined navigate={~p"/open/storage"}>
Open
</.link>
<.link class="button-base button-blue" patch={~p"/new"}>
<.remix_icon icon="add-line" class="align-middle mr-1" />
</.button>
<.button color="blue" patch={~p"/new"}>
<.remix_icon icon="add-line" />
<span>New notebook</span>
</.link>
</.button>
</div>
</div>

View file

@ -37,44 +37,47 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
~H"""
<form id="bulk-action-form" phx-submit="bulk_action" phx-target={@myself}>
<div class="mb-4 flex items-center md:items-end justify-between">
<div class="flex flex-row">
<h2 class="uppercase font-semibold text-gray-500 text-sm md:text-base">
Running sessions (<%= length(@sessions) %>)
</h2>
</div>
<div class="flex flex-row">
<.memory_info memory={@memory} />
<%= if @sessions != [] do %>
<.edit_sessions sessions={@sessions} />
<% end %>
<.menu id="sessions-order-menu">
<:toggle>
<button
class="w-28 button-base button-outlined-gray px-4 py-1 flex justify-between items-center"
type="button"
aria-label={"order by - currently ordered by #{order_by_label(@order_by)}"}
>
<span><%= order_by_label(@order_by) %></span>
<.remix_icon icon="arrow-down-s-line" class="text-lg leading-none align-middle ml-1" />
</button>
</:toggle>
<.menu_item :for={order_by <- ["date", "title", "memory"]}>
<button
class={
<h2 class="uppercase font-semibold text-gray-500 text-sm md:text-base">
Running sessions (<%= length(@sessions) %>)
</h2>
<div class="flex items-center gap-4">
<div class="flex gap-2 w-48 justify-end">
<%= if @sessions != [] do %>
<.edit_sessions sessions={@sessions} />
<% end %>
<.menu id="sessions-order-menu">
<:toggle>
<.button
color="gray"
outlined
small
type="button"
aria-label={"order by - currently ordered by #{order_by_label(@order_by)}"}
>
<span><%= order_by_label(@order_by) %></span>
<.remix_icon icon="arrow-down-s-line" class="text-base leading-none" />
</.button>
</:toggle>
<.menu_item :for={order_by <- ["date", "title", "memory"]}>
<button
class={
"#{if order_by == @order_by, do: "text-gray-900", else: "text-gray-500"}"
}
type="button"
role="menuitem"
phx-click={
JS.push("set_order", value: %{order_by: order_by}, target: @myself)
|> sr_message("ordered by #{order_by}")
}
>
<.remix_icon icon={order_by_icon(order_by)} />
<span><%= order_by_label(order_by) %></span>
</button>
</.menu_item>
</.menu>
type="button"
role="menuitem"
phx-click={
JS.push("set_order", value: %{order_by: order_by}, target: @myself)
|> sr_message("ordered by #{order_by}")
}
>
<.remix_icon icon={order_by_icon(order_by)} />
<span><%= order_by_label(order_by) %></span>
</button>
</.menu_item>
</.menu>
</div>
<div class="border-r border-gray-300 h-5" />
<.memory_info memory={@memory} />
</div>
</div>
<.session_list
@ -147,9 +150,9 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
</div>
<.menu id={"session-#{session.id}-menu"}>
<:toggle>
<button class="icon-button" aria-label="open session menu" type="button">
<.remix_icon icon="more-2-fill" class="text-xl" />
</button>
<.icon_button aria-label="open session menu" type="button">
<.remix_icon icon="more-2-fill" />
</.icon_button>
</:toggle>
<.menu_item>
<a
@ -216,7 +219,7 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
assigns = assign(assigns, free: free, used: used, total: total, percentage: percentage)
~H"""
<div class="pr-1 lg:pr-4" role="group" aria-label="memory information">
<div role="group" aria-label="memory information">
<span class="tooltip top" data-tooltip={"#{format_bytes(@free)} available"}>
<svg viewbox="-10 5 50 25" width="30" height="30" xmlns="http://www.w3.org/2000/svg">
<circle
@ -250,31 +253,25 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
defp edit_sessions(assigns) do
~H"""
<div
class="mx-4 mr-2 text-gray-600 flex flex-row gap-1"
role="group"
aria-label="bulk actions for sessions"
>
<div class="text-gray-600 flex flex-row gap-1" role="group" aria-label="bulk actions for sessions">
<.menu id="edit-sessions">
<:toggle>
<button
<.button
id="toggle-edit"
class="w-28 button-base button-outlined-gray px-4 pl-2 py-1"
color="gray"
outlined
small
phx-click={toggle_edit(:on)}
type="button"
aria-label="toggle edit"
>
<.remix_icon icon="list-check-2" class="text-lg leading-none align-middle ml-1" />
<.remix_icon icon="list-check-2" class="text-base leading-none" />
<span>Edit</span>
</button>
<button
class="hidden w-28 button-base button-outlined-gray px-4 py-1 flex justify-between items-center"
data-el-bulk-edit-member
type="button"
>
</.button>
<.button color="gray" outlined small class="hidden" data-el-bulk-edit-member type="button">
<span>Actions</span>
<.remix_icon icon="arrow-down-s-line" class="text-lg leading-none align-middle ml-1" />
</button>
<.remix_icon icon="arrow-down-s-line" class="text-base leading-none" />
</.button>
</:toggle>
<.menu_item>
<button class="text-gray-600" phx-click={toggle_edit(:off)} type="button">

View file

@ -73,14 +73,9 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do
<.emoji_field field={f[:hub_emoji]} label="Emoji" />
</div>
<div>
<button
class="button-base button-blue"
type="submit"
phx-disable-with="Updating..."
disabled={not @changeset.valid?}
>
<.button type="submit" phx-disable-with="Updating..." disabled={not @changeset.valid?}>
Save
</button>
</.button>
</div>
</.form>
</div>
@ -149,32 +144,32 @@ defmodule LivebookWeb.Hub.Edit.PersonalComponent do
phx-change="stamp_validate"
phx-target={@myself}
>
<div class="flex space-x-2">
<div class="flex space-x-2 items-center">
<div class="grow">
<.password_field field={f[:secret_key]} label="Secret key" />
</div>
<div class="mt-6">
<span class="tooltip top" data-tooltip="Generate">
<button
class="button-base button-outlined-gray button-square-icon"
<.button
color="gray"
small
type="button"
phx-click="generate_secret_key"
phx-target={@myself}
>
<.remix_icon icon="refresh-line" class="text-xl" />
</button>
<.remix_icon icon="refresh-line" class="text-xl leading-none py-1" />
</.button>
</span>
</div>
</div>
<div>
<button
class="button-base button-blue"
<.button
type="submit"
phx-disable-with="Updating..."
disabled={not @stamp_changeset.valid?}
>
Save
</button>
</.button>
</div>
</.form>
</div>

View file

@ -135,9 +135,9 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
</div>
<div>
<button class="button-base button-blue" type="submit" phx-disable-with="Updating...">
<.button type="submit" phx-disable-with="Updating...">
Save
</button>
</.button>
</div>
</.form>
</div>
@ -218,14 +218,15 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
This only removes the hub from this machine. You must rejoin to access its features once again.
</p>
</div>
<button
<.button
color="red"
outlined
id="delete-hub"
phx-click={JS.push("delete_hub", value: %{id: @hub.id})}
class="button-base button-outlined-red"
>
<span class="hidden sm:block">Delete hub</span>
<.remix_icon icon="delete-bin-line" class="text-lg sm:hidden" />
</button>
</.button>
</div>
</div>
</div>
@ -292,64 +293,27 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
required for you and invited users to join this organization.
We recommend storing it somewhere safe:
</div>
<div class="w-full">
<div id="teams-key-toggle" class="relative flex">
<input
type="password"
id="teams-key"
readonly
value={@teams_key}
class="input font-mono w-full border-neutral-200 bg-neutral-100 py-2 border-2 pr-8"
/>
<div class="flex items-center absolute inset-y-0 right-1">
<button
class="icon-button"
data-tooltip="Copied to clipboard"
type="button"
aria-label="copy to clipboard"
phx-click={
JS.dispatch("lb:clipcopy", to: "#teams-key")
|> JS.add_class("", transition: {"tooltip top", "", ""}, time: 2000)
}
>
<.remix_icon icon="clipboard-line" class="text-xl" />
</button>
<button
class="icon-button"
data-show
type="button"
aria-label="show password"
phx-click={
JS.remove_attribute("type", to: "#teams-key-toggle input")
|> JS.set_attribute({"type", "text"}, to: "#teams-key-toggle input")
|> toggle_class("hidden", to: "#teams-key-toggle [data-show]")
|> toggle_class("hidden", to: "#teams-key-toggle [data-hide]")
}
>
<.remix_icon icon="eye-line" class="text-xl" />
</button>
<button
class="icon-button hidden"
data-hide
type="button"
aria-label="hide password"
phx-click={
JS.remove_attribute("type", to: "#teams-key-toggle input")
|> JS.set_attribute({"type", "password"}, to: "#teams-key-toggle input")
|> toggle_class("hidden", to: "#teams-key-toggle [data-show]")
|> toggle_class("hidden", to: "#teams-key-toggle [data-hide]")
}
>
<.remix_icon icon="eye-off-line" class="text-xl" />
</button>
</div>
<div class="flex gap-2">
<div class="grow">
<.password_field id="teams-key" name="teams_key" value={@teams_key} readonly />
</div>
<span
data-tooltip="Copied to clipboard"
aria-label="copy to clipboard"
phx-click={
JS.dispatch("lb:clipcopy", to: "#teams-key")
|> JS.add_class("", transition: {"tooltip left", "", ""}, time: 2000)
}
>
<.button color="gray" small type="button">
<.remix_icon icon="clipboard-line" class="text-xl leading-none py-1" />
</.button>
</span>
</div>
<.link :if={@confirm_url} patch={@confirm_url} class="button-base button-blue block text-center">
<.remix_icon class="mr-2" icon="thumb-up-fill" /> I've saved my Teams key in a secure location
</.link>
<.button :if={@confirm_url} patch={@confirm_url}>
<.remix_icon class="mr-1" icon="thumb-up-fill" /> I've saved my Teams key in a secure location
</.button>
</div>
"""
end

View file

@ -9,7 +9,7 @@ defmodule LivebookWeb.Hub.FileSystemFormComponent do
{file_system, assigns} = Map.pop!(assigns, :file_system)
mode = mode(file_system)
button = button(file_system)
button = button_attrs(file_system)
title = title(file_system)
file_system = file_system || %FileSystem.S3{hub_id: assigns.hub.id}
@ -83,13 +83,13 @@ defmodule LivebookWeb.Hub.FileSystemFormComponent do
</p>
<% end %>
<div class="flex space-x-2">
<button class="button-base button-blue" type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon={@button.icon} class="align-middle mr-1" />
<.button type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon={@button.icon} />
<span class="font-normal"><%= @button.label %></span>
</button>
<.link patch={@return_to} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={@return_to}>
Cancel
</.link>
</.button>
</div>
</div>
</.form>
@ -160,6 +160,6 @@ defmodule LivebookWeb.Hub.FileSystemFormComponent do
defp title(nil), do: "Add file storage"
defp title(_), do: "Edit file storage"
defp button(nil), do: %{icon: "add-line", label: "Add"}
defp button(_), do: %{icon: "save-line", label: "Save"}
defp button_attrs(nil), do: %{icon: "add-line", label: "Add"}
defp button_attrs(_), do: %{icon: "save-line", label: "Save"}
end

View file

@ -23,9 +23,9 @@ defmodule LivebookWeb.Hub.FileSystemListComponent do
<div class="flex items-center space-x-2">
<.menu id={"hub-file-system-#{file_system.id}-menu"}>
<:toggle>
<button class="icon-button" aria-label="open file system menu" type="button">
<.remix_icon icon="more-2-fill" class="text-xl" />
</button>
<.icon_button aria-label="open file system menu" type="button">
<.remix_icon icon="more-2-fill" />
</.icon_button>
</:toggle>
<.menu_item>
<.link
@ -60,13 +60,9 @@ defmodule LivebookWeb.Hub.FileSystemListComponent do
</div>
</div>
<div class="flex">
<.link
patch={~p"/hub/#{@hub_id}/file-systems/new"}
class="button-base button-blue"
id="add-file-system"
>
<.button patch={~p"/hub/#{@hub_id}/file-systems/new"} id="add-file-system">
Add file storage
</.link>
</.button>
</div>
</div>
"""

View file

@ -105,13 +105,11 @@ defmodule LivebookWeb.Hub.NewLive do
label="Livebook Teams key"
/>
<button
:if={!@requested_code}
class="button-base button-blue self-start"
phx-disable-with="Loading..."
>
<%= @button_label %>
</button>
<div>
<.button :if={!@requested_code} phx-disable-with="Loading...">
<%= @button_label %>
</.button>
</div>
<div class="invisible"></div>
<div :if={@requested_code} class="flex flex-col rounded-xl bg-gray-50 px-10 py-6 mt-10">
<div class="flex flex-col items-center rounded-xl bg-gray-50">
@ -130,14 +128,10 @@ defmodule LivebookWeb.Hub.NewLive do
<span class="text-sm">
2. Sign in to Livebook Teams and paste the code:
</span>
<div>
<a
href={@verification_uri}
target="_blank"
class="mt-3 button-base button-outlined-gray"
>
<div class="mt-2">
<.button color="gray" outlined href={@verification_uri} target="_blank">
Go to Teams
</a>
</.button>
</div>
</div>
</div>
@ -155,23 +149,19 @@ defmodule LivebookWeb.Hub.NewLive do
id="clipboard"
class="flex items-center justify-between border rounded-lg px-4 py-2.5 bg-white"
>
<div class="icon-button invisible">
<.remix_icon icon="clipboard-line" class="text-lg" />
</div>
<.icon_button class="invisible">
<.remix_icon icon="clipboard-line" />
</.icon_button>
<div
class="text-brand-pink font-semibold text-xl leading-none"
class="mr-4 text-brand-pink font-semibold text-xl leading-none"
id="clipboard-code"
phx-no-format
><%= @content %></div>
<button
class="icon-button ml-4"
phx-click={JS.dispatch("lb:clipcopy", to: "#clipboard-code")}
type="button"
>
<.remix_icon icon="clipboard-line" class="text-lg text-blue-500" />
</button>
<.icon_button phx-click={JS.dispatch("lb:clipcopy", to: "#clipboard-code")} type="button">
<.remix_icon icon="clipboard-line" />
</.icon_button>
</div>
"""
end

View file

@ -18,7 +18,7 @@ defmodule LivebookWeb.Hub.SecretFormComponent do
{:ok,
assign(socket,
title: title(socket),
button: button(socket),
button: button_attrs(socket),
changeset: changeset,
deployment_group_id: assigns[:deployment_group_id],
error_message: nil
@ -69,13 +69,13 @@ defmodule LivebookWeb.Hub.SecretFormComponent do
<.hidden_field field={f[:hub_id]} value={@hub.id} />
<.hidden_field field={f[:deployment_group_id]} value={@deployment_group_id} />
<div class="flex space-x-2">
<button class="button-base button-blue" type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon={@button.icon} class="align-middle mr-1" />
<.button type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon={@button.icon} />
<span class="font-normal"><%= @button.label %></span>
</button>
<.link patch={@return_to} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={@return_to}>
Cancel
</.link>
</.button>
</div>
</div>
</.form>
@ -118,8 +118,8 @@ defmodule LivebookWeb.Hub.SecretFormComponent do
defp title(%{assigns: %{secret_name: nil}}), do: "Add secret"
defp title(_), do: "Edit secret"
defp button(%{assigns: %{secret_name: nil}}), do: %{icon: "add-line", label: "Add"}
defp button(_), do: %{icon: "save-line", label: "Save"}
defp button_attrs(%{assigns: %{secret_name: nil}}), do: %{icon: "add-line", label: "Add"}
defp button_attrs(_), do: %{icon: "save-line", label: "Save"}
defp set_secret(%{assigns: %{secret_name: nil}} = socket, %Secret{} = secret) do
Hubs.create_secret(socket.assigns.hub, secret)

View file

@ -23,20 +23,19 @@ defmodule LivebookWeb.Hub.SecretListComponent do
<:col :let={secret} label="Name"><%= secret.name %></:col>
<:action :let={secret}>
<span class="tooltip left" data-tooltip="Edit">
<.link
<.icon_button
id={"hub-secret-#{secret.name}-edit"}
patch={"/#{@edit_path}/#{secret.name}"}
type="button"
role="menuitem"
class="icon-button"
>
<.remix_icon icon="edit-fill" class="text-lg" />
</.link>
<.remix_icon icon="edit-fill" />
</.icon_button>
</span>
</:action>
<:action :let={secret}>
<span class="tooltip left" data-tooltip="Delete">
<button
<.icon_button
id={"hub-secret-#{secret.name}-delete"}
type="button"
phx-click={
@ -52,19 +51,18 @@ defmodule LivebookWeb.Hub.SecretListComponent do
)
}
role="menuitem"
class="icon-button"
>
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
</:action>
</.table>
</div>
</div>
<div class="flex">
<.link patch={@add_path} class="button-base button-blue" id="add-secret">
<.button patch={@add_path} id="add-secret">
Add secret
</.link>
</.button>
</div>
</div>
"""

View file

@ -11,95 +11,52 @@ defmodule LivebookWeb.Hub.Teams.AgentKeyListComponent do
<.no_entries :if={@agent_keys == []}>
No agent keys in this deployment group yet.
</.no_entries>
<div
:for={agent_key <- @agent_keys}
class="flex items-center justify-between border border-gray-200 rounded-lg p-4"
>
<div class="flex items-center space-x-12">
<.labeled_text label="ID">
<div class="flex h-[40px] items-center text-center">
<%= agent_key.id %>
</div>
</.labeled_text>
<.labeled_text label="Key">
<div id="agent-key-toggle" class="relative lg:w-[480px] w-full">
<input
type="password"
id="agent-key"
readonly
value={agent_key.key}
class="input font-mono w-full border-neutral-200 bg-neutral-100 py-2 border-2 pr-8"
/>
<div class="flex items-center absolute inset-y-0 right-1">
<button
class="icon-button"
data-tooltip="Copied to clipboard"
type="button"
aria-label="copy to clipboard"
phx-click={
JS.dispatch("lb:clipcopy", to: "#agent-key")
|> JS.add_class("", transition: {"tooltip top", "", ""}, time: 2000)
}
>
<.remix_icon icon="clipboard-line" class="text-xl" />
</button>
<button
class="icon-button"
data-show
type="button"
aria-label="show password"
phx-click={
JS.remove_attribute("type", to: "#agent-key-toggle input")
|> JS.set_attribute({"type", "text"}, to: "#agent-key-toggle input")
|> toggle_class("hidden", to: "#agent-key-toggle [data-show]")
|> toggle_class("hidden", to: "#agent-key-toggle [data-hide]")
}
>
<.remix_icon icon="eye-line" class="text-xl" />
</button>
<button
class="icon-button hidden"
data-hide
type="button"
aria-label="hide password"
phx-click={
JS.remove_attribute("type", to: "#agent-key-toggle input")
|> JS.set_attribute({"type", "password"}, to: "#agent-key-toggle input")
|> toggle_class("hidden", to: "#agent-key-toggle [data-show]")
|> toggle_class("hidden", to: "#agent-key-toggle [data-hide]")
}
>
<.remix_icon icon="eye-off-line" class="text-xl" />
</button>
<div :if={@agent_keys != []}>
<.table id="hub-agent-keys-table" rows={@agent_keys}>
<:col :let={agent_key} label="ID"><%= agent_key.id %></:col>
<:col :let={agent_key} label="Key">
<div class="flex flex-nowrap gap-2">
<div class="grow">
<.password_field
id={"agent-key-#{agent_key.id}"}
name="agent_key"
value={agent_key.key}
readonly
/>
</div>
<span
data-tooltip="Copied to clipboard"
aria-label="copy to clipboard"
phx-click={
JS.dispatch("lb:clipcopy", to: "#agent-key-#{agent_key.id}")
|> JS.add_class("", transition: {"tooltip left", "", ""}, time: 2000)
}
>
<.button color="gray" small type="button">
<.remix_icon icon="clipboard-line" class="text-xl leading-none py-1" />
</.button>
</span>
</div>
</.labeled_text>
</div>
<div class="flex items-center space-x-2">
<.menu id={"hub-agent-key-#{agent_key.id}-menu"}>
<:toggle>
<button class="icon-button" aria-label="open deployment group menu" type="button">
<.remix_icon icon="more-2-fill" class="text-xl" />
</button>
</:toggle>
<.menu_item variant={:danger}>
<button
</:col>
<:action :let={agent_key}>
<span class="tooltip left" data-tooltip="Delete">
<.icon_button
id={"hub-agent-key-#{agent_key.id}-delete"}
type="button"
phx-click={
JS.push("delete_agent_key",
value: %{id: agent_key.id},
target: @myself
)
}
role="menuitem"
class="text-red-600"
phx-click={JS.push("delete_agent_key", value: %{id: agent_key.id})}
phx-target={@myself}
>
<.remix_icon icon="delete-bin-line" />
<span>Delete</span>
</button>
</.menu_item>
</.menu>
</div>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
</:action>
</.table>
</div>
</div>
</div>

View file

@ -20,7 +20,7 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
changeset: changeset,
mode: mode(deployment_group),
title: title(deployment_group),
button: button(deployment_group),
button: button_attrs(deployment_group),
subtitle: subtitle(deployment_group, hub.hub_name),
error_message: nil
)}
@ -72,14 +72,14 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
/>
<LivebookWeb.AppComponents.deployment_group_form_content hub={@hub} form={f} />
<div class="flex space-x-2">
<button class="button-base button-blue" type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon={@button.icon} class="align-middle mr-1" />
<.button type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon={@button.icon} />
<span class="font-normal"><%= @button.label %></span>
</button>
</.button>
<%= if @mode == :new do %>
<.link patch={@return_to} class="button-base button-outlined-gray">
<.button color="gray" outlined patch={@return_to}>
Cancel
</.link>
</.button>
<% end %>
</div>
</div>
@ -145,6 +145,6 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
defp subtitle(%DeploymentGroup{name: deployment_group}, _),
do: "Manage the #{deployment_group} deployment group"
defp button(%DeploymentGroup{name: nil}), do: %{icon: "add-line", label: "Add"}
defp button(_), do: %{icon: "save-line", label: "Save"}
defp button_attrs(%DeploymentGroup{name: nil}), do: %{icon: "add-line", label: "Add"}
defp button_attrs(_), do: %{icon: "save-line", label: "Save"}
end

View file

@ -22,9 +22,9 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupListComponent do
<div class="flex items-center space-x-2">
<.menu id={"hub-deployment-group-#{deployment_group.id}-menu"}>
<:toggle>
<button class="icon-button" aria-label="open deployment group menu" type="button">
<.remix_icon icon="more-2-fill" class="text-xl" />
</button>
<.icon_button aria-label="open deployment group menu" type="button">
<.remix_icon icon="more-2-fill" />
</.icon_button>
</:toggle>
<.menu_item>
<.link
@ -59,13 +59,9 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupListComponent do
</div>
</div>
<div class="flex">
<.link
patch={~p"/hub/#{@hub_id}/deployment-groups/new"}
class="button-base button-blue"
id="add-deployment-group"
>
<.button patch={~p"/hub/#{@hub_id}/deployment-groups/new"} id="add-deployment-group">
Add deployment group
</.link>
</.button>
</div>
</div>
"""

View file

@ -141,14 +141,9 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupLive do
/>
<div class="flex">
<button
id="add-agent-key"
type="button"
class="button-base button-blue"
phx-click="add_agent_key"
>
<.button id="add-agent-key" type="button" phx-click="add_agent_key">
<span>Add agent key</span>
</button>
</.button>
</div>
</div>

View file

@ -50,12 +50,9 @@ defmodule LivebookWeb.LearnLive do
<%= @lead_notebook_info.details.description %>
</p>
<div class="mt-4">
<.link
patch={~p"/learn/notebooks/#{@lead_notebook_info.slug}"}
class="button-base button-blue"
>
<.button patch={~p"/learn/notebooks/#{@lead_notebook_info.slug}"}>
Open notebook
</.link>
</.button>
</div>
</div>
</div>
@ -101,12 +98,9 @@ defmodule LivebookWeb.LearnLive do
<div class="grow text-gray-800 font-semibold">
<%= notebook_info.title %>
</div>
<.link
navigate={~p"/learn/notebooks/#{notebook_info.slug}"}
class="button-base button-outlined-gray"
>
<.remix_icon icon="play-circle-line" class="align-middle mr-1" /> Open
</.link>
<.button color="gray" outlined navigate={~p"/learn/notebooks/#{notebook_info.slug}"}>
<.remix_icon icon="play-circle-line" /> Open
</.button>
</li>
</ul>
</div>

View file

@ -38,19 +38,19 @@ defmodule LivebookWeb.OpenLive do
saved_hubs={@saved_hubs}
>
<:topbar_action>
<.link class="button-base button-blue" navigate={~p"/new"}>
<.remix_icon icon="add-line" class="align-middle mr-1" />
<.button color="blue" navigate={~p"/new"}>
<.remix_icon icon="add-line" />
<span>New notebook</span>
</.link>
</.button>
</:topbar_action>
<div class="p-4 md:px-12 md:py-6 max-w-screen-lg mx-auto space-y-4">
<div class="flex flex-row space-y-0 items-center pb-4 justify-between">
<LayoutComponents.title text="Open notebook" back_navigate={~p"/"} />
<div class="hidden md:flex" role="navigation" aria-label="new notebook">
<.link class="button-base button-blue" navigate={~p"/new"}>
<.remix_icon icon="add-line" class="align-middle mr-1" />
<.button color="blue" navigate={~p"/new"}>
<.remix_icon icon="add-line" />
<span>New notebook</span>
</.link>
</.button>
</div>
</div>

View file

@ -37,29 +37,29 @@ defmodule LivebookWeb.OpenLive.FileComponent do
target={{__MODULE__, @id}}
>
<div class="flex justify-end space-x-2">
<button
class="button-base button-outlined-gray whitespace-nowrap"
<.button
color="gray"
outlined
phx-click="fork"
phx-target={@myself}
disabled={not path_forkable?(@file, @file_info)}
>
<.remix_icon icon="git-branch-line" class="align-middle mr-1" />
<.remix_icon icon="git-branch-line" />
<span>Fork</span>
</button>
</.button>
<%= if session = session_by_file(@file, @sessions) do %>
<.link navigate={~p"/sessions/#{session.id}"} class="button-base button-blue">
<.button navigate={~p"/sessions/#{session.id}"}>
Join session
</.link>
</.button>
<% else %>
<span {open_button_tooltip_attrs(@file, @file_info)}>
<button
class="button-base button-blue"
<.button
phx-click="open"
phx-target={@myself}
disabled={not path_openable?(@file, @file_info, @sessions)}
>
Open
</button>
</.button>
</span>
<% end %>
</div>

View file

@ -37,15 +37,16 @@ defmodule LivebookWeb.OpenLive.SourceComponent do
<.textarea_field
field={f[:source]}
label="Notebook source"
resizable={false}
autofocus
aria-labelledby="import-from-source"
spellcheck="false"
rows="5"
/>
<button class="mt-5 button-base button-blue" type="submit" disabled={not @changeset.valid?}>
Import
</button>
<div class="mt-5">
<.button type="submit" disabled={not @changeset.valid?}>
Import
</.button>
</div>
</.form>
</div>
"""

View file

@ -29,13 +29,11 @@ defmodule LivebookWeb.OpenLive.UploadComponent do
You can only upload files with .livemd extension.
</div>
<% end %>
<button
type="submit"
class="mt-5 button-base button-blue"
disabled={@error or upload_disabled?(@uploads.file)}
>
Import
</button>
<div class="mt-5">
<.button type="submit" disabled={@error or upload_disabled?(@uploads.file)}>
Import
</.button>
</div>
</form>
</div>
"""

View file

@ -58,9 +58,11 @@ defmodule LivebookWeb.OpenLive.UrlComponent do
aria-labelledby="import-from-url"
spellcheck="false"
/>
<button class="mt-5 button-base button-blue" type="submit" disabled={not @changeset.valid?}>
Import
</button>
<div class="mt-5">
<.button type="submit" disabled={not @changeset.valid?}>
Import
</.button>
</div>
</.form>
</div>
"""

View file

@ -269,12 +269,9 @@ defmodule LivebookWeb.Output do
<.remix_icon icon="close-circle-line" />
<span>Missing secret <%= inspect(@secret_name) %></span>
</div>
<.link
patch={~p"/sessions/#{@session_id}/secrets?secret_name=#{@secret_name}"}
class="button-base button-gray"
>
<.button color="gray" patch={~p"/sessions/#{@session_id}/secrets?secret_name=#{@secret_name}"}>
Add secret
</.link>
</.button>
</div>
<%= render_formatted_error_message(@id, @message) %>
</div>
@ -302,12 +299,12 @@ defmodule LivebookWeb.Output do
<.remix_icon icon="close-circle-line" />
<span>Forbidden access to file <%= inspect(@file_entry_name) %></span>
</div>
<button
class="button-base button-gray"
<.button
color="gray"
phx-click={JS.push("review_file_entry_access", value: %{name: @file_entry_name})}
>
Review access
</button>
</.button>
</div>
<%= render_formatted_error_message(@id, @message) %>
</div>
@ -333,7 +330,7 @@ defmodule LivebookWeb.Output do
</div>
<button
class={[
"button-base bg-transparent",
"px-5 py-2 font-medium text-sm inline-flex rounded-lg border whitespace-nowrap items-center justify-center gap-1",
case @variant do
:error -> "border-red-400 text-red-400 hover:bg-red-50 focus:bg-red-50"
:normal -> "border-gray-300 text-gray-500 hover:bg-gray-100 focus:bg-gray-100"
@ -342,7 +339,7 @@ defmodule LivebookWeb.Output do
phx-click="queue_interrupted_cell_evaluation"
phx-value-cell_id={@cell_id}
>
<.remix_icon icon="play-circle-fill" class="align-middle mr-1" />
<.remix_icon icon="play-circle-fill" />
<span>Continue</span>
</button>
</div>
@ -389,12 +386,9 @@ defmodule LivebookWeb.Output do
phx-no-format
><%= LivebookWeb.ANSIHelpers.ansi_string_to_html(@message) %></div>
<div class="absolute right-2 top-0 z-10 invisible group-hover/error:visible">
<button
class="icon-button bg-gray-100"
phx-click={JS.dispatch("lb:clipcopy", to: "##{@id}-message")}
>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>
<.icon_button phx-click={JS.dispatch("lb:clipcopy", to: "##{@id}-message")}>
<.remix_icon icon="clipboard-line" />
</.icon_button>
</div>
</div>
"""

View file

@ -76,38 +76,26 @@ defmodule LivebookWeb.Output.AudioInputComponent do
/>
<audio controls data-preview></audio>
<div class="flex items-center justify-center gap-4">
<button
class="button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500"
data-btn-record
>
<.remix_icon icon="mic-line" class="text-lg leading-none mr-2" />
<.button color="gray" data-btn-record>
<.remix_icon icon="mic-line" />
<span>Record</span>
</button>
<button
class="hidden button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500 items-center"
data-btn-stop
>
<span class="mr-2 flex h-3 w-3 relative">
</.button>
<.button color="gray" class="hidden" data-btn-stop>
<span class="flex h-2 w-2 relative">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-gray-400 opacity-75">
</span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-gray-500"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-gray-500"></span>
</span>
<span>Stop recording</span>
</button>
<button
class="hidden button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500"
data-btn-cancel
>
<.remix_icon icon="close-circle-line" class="text-lg leading-none mr-2" />
</.button>
<.button color="gray" class="hidden" data-btn-cancel>
<.remix_icon icon="close-circle-line" />
<span>Cancel</span>
</button>
<button
class="button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500"
data-btn-upload
>
<.remix_icon icon="upload-2-line" class="text-lg leading-none mr-2" />
</.button>
<.button color="gray" data-btn-upload>
<.remix_icon icon="upload-2-line" />
<span>Upload</span>
</button>
</.button>
</div>
</div>
<form phx-change="validate" class="hidden" phx-target={@myself}>

View file

@ -20,16 +20,15 @@ defmodule LivebookWeb.Output.ControlComponent do
data-p-target={hook_prop(@myself)}
>
<span class="tooltip right" data-tooltip="Toggle keyboard control">
<button
class={
"button-base #{if @keyboard_enabled, do: "button-blue", else: "button-gray"} button-square-icon"
}
<.button
color={if(@keyboard_enabled, do: "blue", else: "gray")}
small
type="button"
aria-label="toggle keyboard control"
phx-click={JS.push("toggle_keyboard", target: @myself)}
>
<.remix_icon icon="keyboard-line" />
</button>
<.remix_icon icon="keyboard-line" class="text-xl leading-none py-1" />
</.button>
</span>
</div>
"""
@ -38,13 +37,9 @@ defmodule LivebookWeb.Output.ControlComponent do
def render(assigns) when assigns.control.attrs.type == :button do
~H"""
<div class="flex">
<button
class="button-base button-gray"
type="button"
phx-click={JS.push("button_click", target: @myself)}
>
<.button color="gray" type="button" phx-click={JS.push("button_click", target: @myself)}>
<%= @control.attrs.label %>
</button>
</.button>
</div>
"""
end

View file

@ -47,9 +47,9 @@ defmodule LivebookWeb.Output.ControlFormComponent do
local={true}
/>
<div :if={@control.attrs.submit}>
<button class="button-base button-blue" type="button" phx-click="submit" phx-target={@myself}>
<.button type="button" phx-click="submit" phx-target={@myself}>
<%= @control.attrs.submit %>
</button>
</.button>
</div>
</div>
"""

View file

@ -82,13 +82,10 @@ defmodule LivebookWeb.Output.ImageInputComponent do
<div class="mt-4 flex items-center justify-center gap-4">
<.menu id={"#{@id}-camera-select-menu"} position={:bottom_left}>
<:toggle>
<button
class="button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500"
data-btn-open-camera
>
<.remix_icon icon="camera-line" class="text-lg leading-none mr-2" />
<.button color="gray" data-btn-open-camera>
<.remix_icon icon="camera-line" />
<span>Open camera</span>
</button>
</.button>
</:toggle>
<div data-camera-list>
<.menu_item>
@ -98,27 +95,18 @@ defmodule LivebookWeb.Output.ImageInputComponent do
</.menu_item>
</div>
</.menu>
<button
class="hidden button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500"
data-btn-capture-camera
>
<.remix_icon icon="camera-line" class="text-lg leading-none mr-2" />
<.button color="gray" class="hidden" data-btn-capture-camera>
<.remix_icon icon="camera-line" />
<span>Take photo</span>
</button>
<button
class="hidden button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500"
data-btn-cancel
>
<.remix_icon icon="close-circle-line" class="text-lg leading-none mr-2" />
</.button>
<.button color="gray" class="hidden" data-btn-cancel>
<.remix_icon icon="close-circle-line" />
<span>Cancel</span>
</button>
<button
class="button-base button-gray border-transparent py-2 px-4 inline-flex text-gray-500"
data-btn-upload
>
<.remix_icon icon="upload-2-line" class="text-lg leading-none mr-2" />
</.button>
<.button color="gray" data-btn-upload>
<.remix_icon icon="upload-2-line" />
<span>Upload</span>
</button>
</.button>
</div>
</div>
<form phx-change="validate" class="hidden" phx-target={@myself}>

View file

@ -92,20 +92,23 @@ defmodule LivebookWeb.Output.InputComponent do
changed={@changed}
help="Choose the time in your local time zone"
/>
<input
id={@id}
type="datetime-local"
data-el-input
class="input w-auto invalid:input--error"
name="html_value"
step="60"
autocomplete="off"
phx-hook="UtcDateTimeInput"
data-p-utc-value={hook_prop(@value && NaiveDateTime.to_iso8601(@value))}
data-p-utc-min={hook_prop(@input.attrs.min && NaiveDateTime.to_iso8601(@input.attrs.min))}
data-p-utc-max={hook_prop(@input.attrs.max && NaiveDateTime.to_iso8601(@input.attrs.max))}
data-p-phx-target={hook_prop(@myself)}
/>
<div class="inline-flex">
<.text_field
class="w-auto"
id={@id}
type="datetime-local"
data-el-input
name="html_value"
step="60"
autocomplete="off"
phx-hook="UtcDateTimeInput"
value={nil}
data-p-utc-value={hook_prop(@value && NaiveDateTime.to_iso8601(@value))}
data-p-utc-min={hook_prop(@input.attrs.min && NaiveDateTime.to_iso8601(@input.attrs.min))}
data-p-utc-max={hook_prop(@input.attrs.max && NaiveDateTime.to_iso8601(@input.attrs.max))}
data-p-phx-target={hook_prop(@myself)}
/>
</div>
</div>
"""
end
@ -118,20 +121,22 @@ defmodule LivebookWeb.Output.InputComponent do
changed={@changed}
help="Choose the time in your local time zone"
/>
<input
id={@id}
type="time"
data-el-input
class="input w-auto invalid:input--error"
name="html_value"
step="60"
autocomplete="off"
phx-hook="UtcTimeInput"
data-p-utc-value={hook_prop(@value && Time.to_iso8601(@value))}
data-p-utc-min={hook_prop(@input.attrs.min && Time.to_iso8601(@input.attrs.min))}
data-p-utc-max={hook_prop(@input.attrs.max && Time.to_iso8601(@input.attrs.max))}
data-p-phx-target={hook_prop(@myself)}
/>
<div class="inline-flex">
<.text_field
id={@id}
type="time"
data-el-input
name="html_value"
step="60"
autocomplete="off"
phx-hook="UtcTimeInput"
value={nil}
data-p-utc-value={hook_prop(@value && Time.to_iso8601(@value))}
data-p-utc-min={hook_prop(@input.attrs.min && Time.to_iso8601(@input.attrs.min))}
data-p-utc-max={hook_prop(@input.attrs.max && Time.to_iso8601(@input.attrs.max))}
data-p-phx-target={hook_prop(@myself)}
/>
</div>
</div>
"""
end
@ -173,7 +178,7 @@ defmodule LivebookWeb.Output.InputComponent do
<input
type="range"
data-el-input
class="input-range"
class="range-input"
name="html_value"
value={@value}
phx-debounce={@attrs.debounce}
@ -191,26 +196,27 @@ defmodule LivebookWeb.Output.InputComponent do
defp input_output(%{attrs: %{type: :textarea}} = assigns) do
~H"""
<textarea
<.textarea_field
id={@id}
class="min-h-[38px] max-h-[300px]"
monospace={@attrs.monospace}
data-el-input
class={["input min-h-[38px] max-h-[300px] tiny-scrollbar", @attrs.monospace && "font-mono"]}
name="html_value"
value={@value}
phx-hook="TextareaAutosize"
phx-debounce={@attrs.debounce}
phx-target={@myself}
spellcheck="false"
><%= [?\n, @value] %></textarea>
/>
"""
end
defp input_output(%{attrs: %{type: :password}} = assigns) do
~H"""
<.with_password_toggle id={"#{@id}-password-toggle"}>
<input
type="password"
<div class="inline-flex">
<.password_field
id={@id}
data-el-input
class="input w-auto bg-gray-50"
name="html_value"
value={@value}
phx-debounce={@attrs.debounce}
@ -218,41 +224,61 @@ defmodule LivebookWeb.Output.InputComponent do
spellcheck="false"
autocomplete="off"
/>
</.with_password_toggle>
</div>
"""
end
defp input_output(%{attrs: %{type: :date}} = assigns) do
~H"""
<input
type="date"
data-el-input
class="input w-auto invalid:input--error"
name="html_value"
value={@value}
phx-debounce="blur"
phx-target={@myself}
min={@attrs.min}
max={@attrs.max}
autocomplete="off"
/>
<div class="inline-flex">
<.text_field
type="date"
data-el-input
name="html_value"
value={@value}
phx-debounce="blur"
phx-target={@myself}
min={@attrs.min}
max={@attrs.max}
autocomplete="off"
/>
</div>
"""
end
defp input_output(%{attrs: %{type: :color}} = assigns) do
~H"""
<div class="w-16">
<.text_field
type="color"
class="h-12"
data-el-input
name="html_value"
value={to_string(@value)}
phx-debounce={@attrs.debounce}
phx-target={@myself}
spellcheck="false"
autocomplete="off"
/>
</div>
"""
end
defp input_output(%{attrs: %{type: type}} = assigns)
when type in [:number, :color, :url, :text] do
when type in [:number, :url, :text] do
~H"""
<input
type={html_input_type(@attrs.type)}
data-el-input
class="input w-auto invalid:input--error"
name="html_value"
value={to_string(@value)}
phx-debounce={@attrs.debounce}
phx-target={@myself}
spellcheck="false"
autocomplete="off"
/>
<div class="inline-flex">
<.text_field
type={html_input_type(@attrs.type)}
data-el-input
name="html_value"
value={to_string(@value)}
phx-debounce={@attrs.debounce}
phx-target={@myself}
spellcheck="false"
autocomplete="off"
/>
</div>
"""
end
@ -282,7 +308,6 @@ defmodule LivebookWeb.Output.InputComponent do
end
defp html_input_type(:number), do: "number"
defp html_input_type(:color), do: "color"
defp html_input_type(:url), do: "url"
defp html_input_type(:text), do: "text"

View file

@ -87,12 +87,9 @@ defmodule LivebookWeb.Output.TerminalTextComponent do
>
</div>
<div class="absolute right-2 top-0 z-10 invisible group-hover/root:visible">
<button
class="icon-button bg-gray-100"
phx-click={JS.dispatch("lb:clipcopy", to: "##{@id}-template")}
>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>
<.icon_button phx-click={JS.dispatch("lb:clipcopy", to: "##{@id}-template")}>
<.remix_icon icon="clipboard-line" />
</.icon_button>
</div>
</div>
"""

View file

@ -81,17 +81,16 @@ defmodule LivebookWeb.SessionLive.AddFileEntryFileComponent do
/>
</div>
<div class="mt-6 flex space-x-3">
<button
class="button-base button-blue"
<.button
type="submit"
disabled={not @changeset.valid? or not regular?(@file, @file_info) or @fetching}
>
<.spinner :if={@fetching} class="mr-2" />
<.spinner :if={@fetching} class="mr-1" />
<span>Add</span>
</button>
<.link patch={~p"/sessions/#{@session.id}"} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}"}>
Cancel
</.link>
</.button>
</div>
</.form>
</div>

View file

@ -69,16 +69,12 @@ defmodule LivebookWeb.SessionLive.AddFileEntryUnlistedComponent do
/>
</div>
<div class="mt-6 flex space-x-3">
<button
class="button-base button-blue"
type="submit"
disabled={Enum.empty?(@selected_indices)}
>
<.button type="submit" disabled={Enum.empty?(@selected_indices)}>
Add
</button>
<.link patch={~p"/sessions/#{@session.id}"} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}"}>
Cancel
</.link>
</.button>
</div>
</form>
</div>

View file

@ -64,17 +64,13 @@ defmodule LivebookWeb.SessionLive.AddFileEntryUploadComponent do
/>
</div>
<div class="mt-6 flex space-x-3">
<button
class="button-base button-blue"
type="submit"
disabled={not @changeset.valid? or upload_disabled?(@uploads.file)}
>
<.spinner class="hidden phx-submit-loading:block mr-2" />
<.button type="submit" disabled={not @changeset.valid? or upload_disabled?(@uploads.file)}>
<.spinner class="hidden phx-submit-loading:block mr-1" />
<span>Add</span>
</button>
<.link patch={~p"/sessions/#{@session.id}"} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}"}>
Cancel
</.link>
</.button>
</div>
</.form>
</div>

View file

@ -63,17 +63,13 @@ defmodule LivebookWeb.SessionLive.AddFileEntryUrlComponent do
/>
</div>
<div class="mt-6 flex space-x-3">
<button
class="button-base button-blue"
type="submit"
disabled={not @changeset.valid? or @fetching}
>
<.spinner :if={@fetching} class="mr-2" />
<.button type="submit" disabled={not @changeset.valid? or @fetching}>
<.spinner :if={@fetching} class="mr-1" />
<span>Add</span>
</button>
<.link patch={~p"/sessions/#{@session.id}"} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}"}>
Cancel
</.link>
</.button>
</div>
</.form>
</div>

View file

@ -171,16 +171,17 @@ defmodule LivebookWeb.SessionLive.AppDockerComponent do
dockerfile_config={apply_changes(@changeset)}
>
<:dockerfile_actions>
<button
class="button-base button-gray whitespace-nowrap py-1 px-2"
<.button
color="gray"
small
type="button"
aria-label="save dockerfile alongside the notebook"
phx-click="save_dockerfile"
phx-target={@myself}
>
<.remix_icon icon="save-line" class="align-middle mr-1 text-xs" />
<span class="font-normal text-xs">Save alongside notebook</span>
</button>
<.remix_icon icon="save-line" />
<span>Save alongside notebook</span>
</.button>
</:dockerfile_actions>
</AppComponents.docker_instructions>
</div>

View file

@ -20,10 +20,10 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
message="This session is a running app. To deploy a modified version, you can fork it."
/>
<div class="mt-6">
<button class="button-base button-blue" phx-click="fork_session">
<.button phx-click="fork_session">
<.remix_icon icon="git-branch-line" />
<span>Fork</span>
</button>
</.button>
</div>
</div>
<% else %>
@ -35,20 +35,16 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
/>
<div class="flex flex-col gap-3">
<div class="flex gap-2">
<button
class="button-base button-blue"
<.button
phx-click="deploy_app"
disabled={not Livebook.Notebook.AppSettings.valid?(@settings)}
>
<.remix_icon icon="rocket-line" class="align-middle mr-1" />
<.remix_icon icon="rocket-line" />
<span>Deploy</span>
</button>
<.link
patch={~p"/sessions/#{@session.id}/settings/app"}
class="button-base button-outlined-gray bg-transparent"
>
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/app"}>
Configure
</.link>
</.button>
</div>
<.link
class="text-sm text-gray-700 hover:text-blue-600"
@ -82,13 +78,12 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
<div class="border-t border-gray-200 px-3 py-2 flex space-x-2">
<div class="grow" />
<span class="tooltip top" data-tooltip="Terminate">
<button
class="icon-button"
<.icon_button
aria-label="terminate app"
phx-click={JS.push("terminate_app", target: @myself)}
>
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
</div>
</div>
@ -114,24 +109,23 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
</div>
<div class="border-t border-gray-200 px-3 py-2 flex space-x-2">
<span class="tooltip top" data-tooltip="Open">
<a
class={["icon-button", app_session.app_status.lifecycle != :active && "disabled"]}
<.icon_button
disabled={app_session.app_status.lifecycle}
aria-label="open app"
href={~p"/apps/#{@app.slug}/#{app_session.id}"}
>
<.remix_icon icon="link" class="text-lg" />
</a>
<.remix_icon icon="link" />
</.icon_button>
</span>
<div class="grow" />
<span class="tooltip top" data-tooltip="Debug">
<a class="icon-button" aria-label="debug app" href={~p"/sessions/#{app_session.id}"}>
<.remix_icon icon="terminal-line" class="text-lg" />
</a>
<.icon_button aria-label="debug app" href={~p"/sessions/#{app_session.id}"}>
<.remix_icon icon="terminal-line" />
</.icon_button>
</span>
<%= if app_session.app_status.lifecycle == :active do %>
<span class="tooltip top" data-tooltip="Deactivate">
<button
class="icon-button"
<.icon_button
aria-label="deactivate app session"
phx-click={
JS.push("deactivate_app_session",
@ -140,13 +134,12 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
)
}
>
<.remix_icon icon="stop-circle-line" class="text-lg" />
</button>
<.remix_icon icon="stop-circle-line" />
</.icon_button>
</span>
<% else %>
<span class="tooltip top" data-tooltip="Terminate">
<button
class="icon-button"
<.icon_button
aria-label="terminate app session"
phx-click={
JS.push("terminate_app_session",
@ -155,8 +148,8 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
)
}
>
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
<% end %>
</div>
@ -171,7 +164,7 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
defp app_info_icon(assigns) do
~H"""
<span
class="icon-button p-0 cursor-pointer tooltip bottom-left"
class="tooltip bottom-left"
data-tooltip={
~S'''
App deployment is a way to share your
@ -181,7 +174,9 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
'''
}
>
<.remix_icon icon="question-line" class="text-xl leading-none" />
<.icon_button>
<.remix_icon icon="question-line" />
</.icon_button>
</span>
"""
end

View file

@ -118,18 +118,17 @@ defmodule LivebookWeb.SessionLive.AppSettingsComponent do
<% end %>
</div>
<div class="mt-8 flex space-x-2">
<button
class="button-base button-blue"
<.button
type="button"
phx-click={JS.patch(~p"/sessions/#{@session.id}") |> JS.push("deploy_app")}
disabled={not @changeset.valid?}
>
<.remix_icon icon="rocket-line" class="align-middle mr-1" />
<.remix_icon icon="rocket-line" />
<span>Deploy</span>
</button>
<button class="button-base button-outlined-gray" type="reset" name="reset">
</.button>
<.button color="gray" outlined type="reset" name="reset">
Reset
</button>
</.button>
</div>
</.form>
</div>

View file

@ -78,13 +78,13 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
autocomplete="off"
spellcheck="false"
>
<div class="flex flex-col space-y-4">
<div class="flex flex-col space-y-4 mb-5">
<.text_field field={f[:name]} label="Name" placeholder={name_placeholder()} />
<.text_field field={f[:cookie]} label="Cookie" placeholder="mycookie" />
</div>
<button class="mt-5 button-base button-blue" type="submit" disabled={not @changeset.valid?}>
<.button type="submit" disabled={not @changeset.valid?}>
<%= if(reconnecting?(@changeset), do: "Reconnect", else: "Connect") %>
</button>
</.button>
</.form>
</div>
"""

View file

@ -46,9 +46,8 @@ defmodule LivebookWeb.SessionLive.BinComponent do
<span class="absolute inset-y-0 left-0 pl-3 flex items-center">
<.remix_icon icon="search-line" class="align-bottom text-gray-500" />
</span>
<input
class="input block w-full pl-10"
type="text"
<.text_field
class="pl-10"
name="search"
value={@search}
placeholder="Search"
@ -113,30 +112,32 @@ defmodule LivebookWeb.SessionLive.BinComponent do
source={cell.source}
/>
<div class="pt-1 pb-4 border-b border-gray-200">
<button
class="button-base button-gray whitespace-nowrap py-1 px-2"
<.button
color="gray"
small
aria-label="restore"
phx-click="restore"
phx-value-cell_id={entry.cell.id}
phx-target={@myself}
>
<.remix_icon icon="arrow-go-back-line" class="align-middle mr-1 text-xs" />
<span class="font-normal text-xs">Restore</span>
</button>
<button
class="button-base button-gray whitespace-nowrap py-1 px-2"
<.remix_icon icon="arrow-go-back-line" />
<span>Restore</span>
</.button>
<.button
color="gray"
small
aria-label="copy source"
phx-click={JS.dispatch("lb:clipcopy", to: "#bin-cell-#{cell.id}-source")}
>
<.remix_icon icon="clipboard-line" class="align-middle mr-1 text-xs" />
<span class="font-normal text-xs">Copy source</span>
</button>
<.remix_icon icon="clipboard-line" />
<span>Copy source</span>
</.button>
</div>
</div>
<div :if={length(@matching_entries) > @limit} class="flex justify-center">
<button class="button-base button-outlined-gray" phx-click="more" phx-target={@myself}>
<.button color="gray" outlined phx-click="more" phx-target={@myself}>
Older
</button>
</.button>
</div>
</div>
<% end %>

View file

@ -245,12 +245,12 @@ defmodule LivebookWeb.SessionLive.CellComponent do
<span>
The Smart cell crashed unexpectedly, this is most likely a bug.
</span>
<button
class="button-base button-gray"
<.button
color="gray"
phx-click={JS.push("recover_smart_cell", value: %{cell_id: @cell_view.id})}
>
Restart Smart cell
</button>
</.button>
</div>
<% :starting -> %>
<div class="delay-200">
@ -450,9 +450,9 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp enable_insert_mode_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Edit content" data-el-enable-insert-mode-button>
<button class="icon-button" aria-label="edit content">
<.remix_icon icon="pencil-line" class="text-xl" />
</button>
<.icon_button aria-label="edit content">
<.remix_icon icon="pencil-line" />
</.icon_button>
</span>
"""
end
@ -460,9 +460,9 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp toggle_source_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Toggle source" data-el-toggle-source-button>
<button class="icon-button" aria-label="toggle source">
<.remix_icon icon="code-line" class="text-xl" />
</button>
<.icon_button aria-label="toggle source">
<.remix_icon icon="code-line" />
</.icon_button>
</span>
"""
end
@ -470,14 +470,13 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp convert_smart_cell_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Convert to Code cell">
<button
class="icon-button"
<.icon_button
aria-label="toggle source"
data-link-package-search
phx-click={JS.push("convert_smart_cell", value: %{cell_id: @cell_id})}
>
<.remix_icon icon="pencil-line" class="text-xl" />
</button>
<.remix_icon icon="pencil-line" />
</.icon_button>
</span>
"""
end
@ -489,20 +488,19 @@ defmodule LivebookWeb.SessionLive.CellComponent do
class="tooltip top"
data-tooltip="The current runtime does not support adding dependencies"
>
<button class="icon-button" disabled>
<.remix_icon icon="play-list-add-line" class="text-xl" />
</button>
<.icon_button disabled>
<.remix_icon icon="play-list-add-line" />
</.icon_button>
</span>
<% else %>
<span class="tooltip top" data-tooltip="Add package (sp)">
<.link
<.icon_button
patch={~p"/sessions/#{@session_id}/package-search"}
class="icon-button"
role="button"
data-btn-package-search
>
<.remix_icon icon="play-list-add-line" class="text-xl" />
</.link>
<.remix_icon icon="play-list-add-line" />
</.icon_button>
</span>
<% end %>
"""
@ -511,9 +509,9 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp cell_link_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Link">
<a href={"#cell-#{@cell_id}"} class="icon-button" role="button" aria-label="link to cell">
<.remix_icon icon="link" class="text-xl" />
</a>
<.icon_button href={"#cell-#{@cell_id}"} role="button" aria-label="link to cell">
<.remix_icon icon="link" />
</.icon_button>
</span>
"""
end
@ -521,9 +519,9 @@ defmodule LivebookWeb.SessionLive.CellComponent do
def amplify_output_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Amplify output" data-el-amplify-outputs-button>
<button class="icon-button" aria-label="amplify outputs">
<.remix_icon icon="zoom-in-line" class="text-xl" />
</button>
<.icon_button aria-label="amplify outputs">
<.remix_icon icon="zoom-in-line" />
</.icon_button>
</span>
"""
end
@ -531,14 +529,13 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp cell_settings_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Cell settings">
<.link
<.icon_button
patch={~p"/sessions/#{@session_id}/cell-settings/#{@cell_id}"}
class="icon-button"
aria-label="cell settings"
role="button"
>
<.remix_icon icon="settings-3-line" class="text-xl" />
</.link>
<.remix_icon icon="settings-3-line" />
</.icon_button>
</span>
"""
end
@ -546,15 +543,14 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp move_cell_up_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Move up">
<button
class="icon-button"
<.icon_button
aria-label="move cell up"
phx-click="move_cell"
phx-value-cell_id={@cell_id}
phx-value-offset="-1"
>
<.remix_icon icon="arrow-up-s-line" class="text-xl" />
</button>
<.remix_icon icon="arrow-up-s-line" />
</.icon_button>
</span>
"""
end
@ -562,15 +558,14 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp move_cell_down_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Move down">
<button
class="icon-button"
<.icon_button
aria-label="move cell down"
phx-click="move_cell"
phx-value-cell_id={@cell_id}
phx-value-offset="1"
>
<.remix_icon icon="arrow-down-s-line" class="text-xl" />
</button>
<.remix_icon icon="arrow-down-s-line" />
</.icon_button>
</span>
"""
end
@ -578,13 +573,12 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp delete_cell_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Delete">
<button
class="icon-button"
<.icon_button
aria-label="delete cell"
phx-click={JS.push("delete_cell", value: %{cell_id: @cell_id})}
>
<.remix_icon icon="delete-bin-6-line" class="text-xl" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
"""
end
@ -601,9 +595,9 @@ defmodule LivebookWeb.SessionLive.CellComponent do
'''
}
>
<span class="icon-button">
<.remix_icon icon="question-line" class="text-xl" />
</span>
<.icon_button>
<.remix_icon icon="question-line" />
</.icon_button>
</span>
"""
end

View file

@ -47,12 +47,12 @@ defmodule LivebookWeb.SessionLive.CodeCellSettingsComponent do
/>
</div>
<div class="mt-8 flex justify-begin space-x-2">
<button class="button-base button-blue" type="submit">
<.button type="submit">
Save
</button>
<.link patch={@return_to} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={@return_to}>
Cancel
</.link>
</.button>
</div>
</form>
</div>

View file

@ -27,9 +27,9 @@ defmodule LivebookWeb.SessionLive.CustomViewComponent do
<.switch_field name="spotlight" label="Spotlight focused" value={false} />
</div>
<div class="mt-8 flex justify-end space-x-2">
<.link patch={@return_to} class="button-base button-outlined-gray">
<.button color="gray" outlined patch={@return_to}>
Close
</.link>
</.button>
</div>
</div>
"""

View file

@ -32,9 +32,9 @@ defmodule LivebookWeb.SessionLive.ElixirStandaloneLive do
<p class="text-gray-700">
Start a new local node to handle code evaluation.
</p>
<button class="button-base button-blue" phx-click="init">
<.button phx-click="init">
<%= if(matching_runtime?(@current_runtime), do: "Reconnect", else: "Connect") %>
</button>
</.button>
</div>
"""
end

View file

@ -39,9 +39,9 @@ defmodule LivebookWeb.SessionLive.EmbeddedLive do
you restart Livebook. Furthermore, code in one notebook
may interfere with code from another notebook.
</p>
<button class="button-base button-blue" phx-click="init">
<.button phx-click="init">
<%= if(matching_runtime?(@current_runtime), do: "Reconnect", else: "Connect") %>
</button>
</.button>
</div>
"""
end

View file

@ -24,23 +24,21 @@ defmodule LivebookWeb.SessionLive.ExportElixirComponent do
</span>
<div class="flex justify-end space-x-2">
<span class="tooltip left" data-tooltip="Copy source">
<button
class="icon-button"
<.icon_button
aria-label="copy source"
phx-click={JS.dispatch("lb:clipcopy", to: "#export-notebook-source")}
>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>
<.remix_icon icon="clipboard-line" />
</.icon_button>
</span>
<span class="tooltip left" data-tooltip="Download source">
<a
class="icon-button"
<.icon_button
aria-label="download source"
href={~p"/sessions/#{@session.id}/download/export/exs"}
download
>
<.remix_icon icon="download-2-line" class="text-lg" />
</a>
<.remix_icon icon="download-2-line" />
</.icon_button>
</span>
</div>
</div>

View file

@ -43,25 +43,23 @@ defmodule LivebookWeb.SessionLive.ExportLiveMarkdownComponent do
</span>
<div class="flex justify-end space-x-2">
<span class="tooltip left" data-tooltip="Copy source">
<button
class="icon-button"
<.icon_button
aria-label="copy source"
phx-click={JS.dispatch("lb:clipcopy", to: "#export-notebook-source")}
>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>
<.remix_icon icon="clipboard-line" />
</.icon_button>
</span>
<span class="tooltip left" data-tooltip="Download source">
<a
class="icon-button"
<.icon_button
aria-label="download source"
href={
~p"/sessions/#{@session.id}/download/export/livemd?include_outputs=#{@include_outputs}"
}
download
>
<.remix_icon icon="download-2-line" class="text-lg" />
</a>
<.remix_icon icon="download-2-line" />
</.icon_button>
</span>
</div>
</div>

View file

@ -126,9 +126,9 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do
<% else %>
<.menu id={"file-entry-#{file_entry.type}-#{idx}-menu"} position={:bottom_right}>
<:toggle>
<button class="icon-button" aria-label="menu">
<.icon_button small aria-label="menu">
<.remix_icon icon="more-2-line" />
</button>
</.icon_button>
</:toggle>
<.menu_item>
<button
@ -209,7 +209,7 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do
defp references_info_icon(assigns) do
~H"""
<span
class="icon-button cursor-pointer tooltip bottom-left"
class="tooltip bottom-left"
data-tooltip={
~S'''
References are files that point to
@ -218,7 +218,9 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do
'''
}
>
<.remix_icon icon="question-line" class="leading-none text-gray-700" />
<.icon_button small>
<.remix_icon icon="question-line" />
</.icon_button>
</span>
"""
end
@ -226,7 +228,7 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do
defp attachments_info_icon(assigns) do
~H"""
<span
class="icon-button cursor-pointer tooltip bottom-left"
class="tooltip bottom-left"
data-tooltip={
~S'''
Attachments are files stored in the
@ -235,7 +237,9 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do
'''
}
>
<.remix_icon icon="question-line" class="leading-none text-gray-700" />
<.icon_button small>
<.remix_icon icon="question-line" />
</.icon_button>
</span>
"""
end

View file

@ -27,7 +27,7 @@ defmodule LivebookWeb.SessionLive.InsertButtonsComponent do
}>
<.menu id={"cell-#{@id}-insert"} position={:bottom_left} distant>
<:toggle>
<button class="button-base button-small flex items-center pr-1">
<.insert_button>
<div
class="pr-2"
phx-click="insert_cell_below"
@ -37,10 +37,10 @@ defmodule LivebookWeb.SessionLive.InsertButtonsComponent do
>
+ <%= @default_language |> Atom.to_string() |> String.capitalize() %>
</div>
<div class="pl-1 flex items-center border-l border-gray-200">
<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>
</button>
</.insert_button>
</:toggle>
<.menu_item>
<button
@ -71,7 +71,7 @@ defmodule LivebookWeb.SessionLive.InsertButtonsComponent do
</.menu>
<.menu id={"#{@id}-block-menu"} position={:bottom_left}>
<:toggle>
<button class="button-base button-small">+ Block</button>
<.insert_button>+ Block</.insert_button>
</:toggle>
<.menu_item>
<button
@ -149,24 +149,21 @@ defmodule LivebookWeb.SessionLive.InsertButtonsComponent do
</.menu>
<%= cond do %>
<% not Livebook.Runtime.connected?(@runtime) -> %>
<button
class="button-base button-small"
phx-click={
JS.push("setup_default_runtime",
value: %{reason: "To see the available smart cells, you need a connected runtime."}
)
}
>
<.insert_button phx-click={
JS.push("setup_default_runtime",
value: %{reason: "To see the available smart cells, you need a connected runtime."}
)
}>
+ Smart
</button>
</.insert_button>
<% @smart_cell_definitions == [] -> %>
<span class="tooltip right" data-tooltip="No smart cells available">
<button class="button-base button-small" disabled>+ Smart</button>
<.insert_button disabled>+ Smart</.insert_button>
</span>
<% true -> %>
<.menu id={"#{@id}-smart-menu"} position={:bottom_left}>
<:toggle>
<button class="button-base button-small">+ Smart</button>
<.insert_button>+ Smart</.insert_button>
</:toggle>
<.menu_item :for={definition <- @smart_cell_definitions}>
<.smart_cell_insert_button
@ -182,6 +179,29 @@ defmodule LivebookWeb.SessionLive.InsertButtonsComponent do
"""
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>

View file

@ -69,16 +69,12 @@ defmodule LivebookWeb.SessionLive.InsertImageComponent do
/>
</div>
<div class="mt-8 flex justify-end space-x-2">
<.link patch={@return_to} class="button-base button-outlined-gray">
<.button color="gray" outlined patch={@return_to}>
Cancel
</.link>
<button
class="button-base button-blue"
type="submit"
disabled={not @changeset.valid? or upload_disabled?(@uploads.image)}
>
</.button>
<.button type="submit" disabled={not @changeset.valid? or upload_disabled?(@uploads.image)}>
Upload
</button>
</.button>
</div>
</.form>
</div>

View file

@ -29,8 +29,7 @@ defmodule LivebookWeb.SessionLive.PackageSearchLive do
Find external packages for your notebook
</p>
<form phx-submit="submit" phx-change="search">
<input
class="input"
<.text_field
name="search"
value={@search}
phx-debounce="250"
@ -78,14 +77,10 @@ defmodule LivebookWeb.SessionLive.PackageSearchLive do
</div>
</div>
<div class="ml-2">
<button
class="button-base button-gray whitespace-nowrap py-1 px-2"
aria-label="add"
phx-click={JS.push("add", value: %{idx: @idx})}
>
<.remix_icon icon="add-line" class="align-middle mr-1 text-xs" />
<span class="font-normal text-xs">Add</span>
</button>
<.button color="gray" small aria-label="add" phx-click={JS.push("add", value: %{idx: @idx})}>
<.remix_icon icon="add-line" />
<span>Add</span>
</.button>
</div>
</div>
"""

View file

@ -119,26 +119,20 @@ defmodule LivebookWeb.SessionLive.PersistenceComponent do
</div>
<div class="flex justify-between">
<div class="flex space-x-3">
<button
class="button-base button-blue"
<.button
phx-click="save"
phx-target={@myself}
disabled={not savable?(@draft_file, @saved_file, @running_files)}
>
Save
</button>
<.link patch={~p"/sessions/#{@session.id}"} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}"}>
Cancel
</.link>
</.button>
</div>
<button
:if={@saved_file}
class="button-base button-outlined-red"
phx-click="stop_saving"
phx-target={@myself}
>
<.button :if={@saved_file} color="red" outlined phx-click="stop_saving" phx-target={@myself}>
Stop saving to file
</button>
</.button>
</div>
</div>
"""

View file

@ -47,13 +47,13 @@ defmodule LivebookWeb.SessionLive.RenameFileEntryComponent do
autofocus
/>
<div class="mt-6 flex space-x-3">
<button class="button-base button-blue" type="submit" disabled={not @changeset.valid?}>
<.button type="submit" disabled={not @changeset.valid?}>
<.spinner class="hidden phx-submit-loading:block mr-2" />
<span>Rename</span>
</button>
<.link patch={~p"/sessions/#{@session.id}"} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}"}>
Cancel
</.link>
</.button>
</div>
</.form>
</div>

View file

@ -563,13 +563,9 @@ defmodule LivebookWeb.SessionLive.Render do
</span>
</button>
<%= if client_id == @client_id do %>
<button
class="icon-button"
aria-label="edit profile"
phx-click={show_current_user_modal()}
>
<.remix_icon icon="user-settings-line" class="text-lg" />
</button>
<.icon_button aria-label="edit profile" phx-click={show_current_user_modal()}>
<.remix_icon icon="user-settings-line" />
</.icon_button>
<% else %>
<span
class="tooltip left"
@ -577,9 +573,9 @@ defmodule LivebookWeb.SessionLive.Render do
data-el-client-follow-toggle
data-meta="follow"
>
<button class="icon-button" aria-label="follow this user">
<.remix_icon icon="pushpin-line" class="text-lg" />
</button>
<.icon_button aria-label="follow this user">
<.remix_icon icon="pushpin-line" />
</.icon_button>
</span>
<span
class="tooltip left"
@ -587,9 +583,9 @@ defmodule LivebookWeb.SessionLive.Render do
data-el-client-follow-toggle
data-meta="unfollow"
>
<button class="icon-button" aria-label="unfollow this user">
<.remix_icon icon="pushpin-fill" class="text-lg" />
</button>
<.icon_button aria-label="unfollow this user">
<.remix_icon icon="pushpin-fill" />
</.icon_button>
</span>
<% end %>
</div>
@ -605,9 +601,9 @@ defmodule LivebookWeb.SessionLive.Render do
<h3 class="uppercase text-sm font-semibold text-gray-500">
Runtime
</h3>
<.link patch={~p"/sessions/#{@session.id}/settings/runtime"} class="icon-button p-0">
<.remix_icon icon="settings-3-line text-xl" />
</.link>
<.icon_button patch={~p"/sessions/#{@session.id}/settings/runtime"}>
<.remix_icon icon="settings-3-line" />
</.icon_button>
</div>
<div class="flex flex-col mt-2 space-y-4">
<div class="flex flex-col space-y-3">
@ -621,28 +617,21 @@ defmodule LivebookWeb.SessionLive.Render do
</div>
<div class="flex space-x-2">
<%= if Runtime.connected?(@data_view.runtime) do %>
<button class="button-base button-blue" phx-click="reconnect_runtime">
<.remix_icon icon="wireless-charging-line" class="align-middle mr-1" />
<.button phx-click="reconnect_runtime">
<.remix_icon icon="wireless-charging-line" />
<span>Reconnect</span>
</button>
<button
class="button-base button-outlined-red"
type="button"
phx-click="disconnect_runtime"
>
</.button>
<.button color="red" outlined type="button" phx-click="disconnect_runtime">
Disconnect
</button>
</.button>
<% else %>
<button class="button-base button-blue" phx-click="connect_runtime">
<.remix_icon icon="wireless-charging-line" class="align-middle mr-1" />
<.button phx-click="connect_runtime">
<.remix_icon icon="wireless-charging-line" />
<span>Connect</span>
</button>
<.link
patch={~p"/sessions/#{@session.id}/settings/runtime"}
class="button-base button-outlined-gray bg-transparent"
>
</.button>
<.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/runtime"}>
Configure
</.link>
</.button>
<% end %>
</div>
<%= if uses_memory?(@session.memory_usage) do %>
@ -737,9 +726,9 @@ defmodule LivebookWeb.SessionLive.Render do
~H"""
<.menu id="session-menu">
<:toggle>
<button class="icon-button" aria-label="open notebook menu">
<.remix_icon icon="more-2-fill" class="text-xl" />
</button>
<.icon_button aria-label="open notebook menu">
<.remix_icon icon="more-2-fill" />
</.icon_button>
</:toggle>
<.menu_item>
<.link patch={~p"/sessions/#{@session.id}/export/livemd"} role="menuitem">
@ -909,14 +898,14 @@ defmodule LivebookWeb.SessionLive.Render do
<.menu id="views-menu" position={:bottom_right} sm_position={:top_right}>
<:toggle>
<button
class="icon-button icon-outlined-button border-gray-200 hover:bg-gray-100 focus:bg-gray-100"
class={status_button_classes(:gray)}
aria-label="choose views to activate"
data-el-views-disabled
>
<.remix_icon icon="layout-5-line" class="text-xl text-gray-400" />
<.remix_icon icon="layout-5-line" />
</button>
<button
class="icon-button icon-outlined-button border-green-bright-300 hover:bg-green-bright-50 focus:bg-green-bright-50"
class={status_button_classes(:green)}
aria-label="choose views to activate"
data-el-views-enabled
>
@ -951,10 +940,10 @@ defmodule LivebookWeb.SessionLive.Render do
<span class="tooltip left" data-tooltip="Choose a file to save the notebook">
<.link
patch={~p"/sessions/#{@session_id}/settings/file"}
class="icon-button icon-outlined-button border-gray-200 hover:bg-gray-100 focus:bg-gray-100"
class={status_button_classes(:gray)}
aria-label="choose a file to save the notebook"
>
<.remix_icon icon="save-line" class="text-xl text-gray-400" />
<.remix_icon icon="save-line" />
</.link>
</span>
"""
@ -976,15 +965,17 @@ defmodule LivebookWeb.SessionLive.Render do
>
<.link
patch={~p"/sessions/#{@session_id}/settings/file"}
class="icon-button icon-outlined-button border-green-bright-300 hover:bg-green-bright-50 focus:bg-green-bright-50 relative"
class={status_button_classes(:green)}
aria-label="notebook saved, click to open file settings"
>
<.remix_icon icon="save-line" class="text-xl text-green-bright-400" />
<.remix_icon
:if={@persistence_warnings != []}
icon="error-warning-fill"
class="text-lg text-red-400 absolute -top-1.5 -right-2"
/>
<div class="relative">
<.remix_icon icon="save-line" />
<.remix_icon
:if={@persistence_warnings != []}
icon="error-warning-fill"
class="text-lg text-red-400 absolute -top-1.5 -right-2"
/>
</div>
</.link>
</span>
"""
@ -995,10 +986,10 @@ defmodule LivebookWeb.SessionLive.Render do
<span class="tooltip left" data-tooltip="No autosave configured, make sure to save manually">
<.link
patch={~p"/sessions/#{@session_id}/settings/file"}
class="icon-button icon-outlined-button border-yellow-bright-200 hover:bg-red-50 focus:bg-red-50"
class={status_button_classes(:yellow)}
aria-label="no autosave configured, click to open file settings"
>
<.remix_icon icon="save-line" class="text-xl text-yellow-bright-300" />
<.remix_icon icon="save-line" />
</.link>
</span>
"""
@ -1009,10 +1000,10 @@ defmodule LivebookWeb.SessionLive.Render do
<span class="tooltip left" data-tooltip="Autosave pending">
<.link
patch={~p"/sessions/#{@session_id}/settings/file"}
class="icon-button icon-outlined-button border-blue-400 hover:bg-blue-50 focus:bg-blue-50"
class={status_button_classes(:blue)}
aria-label="autosave pending, click to open file settings"
>
<.remix_icon icon="save-line" class="text-xl text-blue-500" />
<.remix_icon icon="save-line" />
</.link>
</span>
"""
@ -1026,10 +1017,10 @@ defmodule LivebookWeb.SessionLive.Render do
<span class="tooltip left" data-tooltip="Choose a runtime to run the notebook in">
<.link
patch={~p"/sessions/#{@session_id}/settings/runtime"}
class="icon-button icon-outlined-button border-gray-200 hover:bg-gray-100 focus:bg-gray-100"
class={status_button_classes(:gray)}
aria-label="choose a runtime to run the notebook in"
>
<.remix_icon icon="loader-3-line" class="text-xl text-gray-400" />
<.remix_icon icon="loader-3-line" />
</.link>
</span>
<% end %>
@ -1040,12 +1031,12 @@ defmodule LivebookWeb.SessionLive.Render do
~H"""
<span class="tooltip left" data-tooltip="Go to evaluating cell">
<button
class="border-blue-400 icon-button icon-outlined-button hover:bg-blue-50 focus:bg-blue-50"
class={status_button_classes(:blue)}
aria-label="go to evaluating cell"
data-el-focus-cell-button
data-target={@cell_id}
>
<.remix_icon icon="loader-3-line" class="text-xl text-blue-500 animate-spin" />
<.remix_icon icon="loader-3-line" class="animate-spin" />
</button>
</span>
"""
@ -1055,12 +1046,12 @@ defmodule LivebookWeb.SessionLive.Render do
~H"""
<span class="tooltip left" data-tooltip="Go to last evaluated cell">
<button
class="border-green-bright-300 icon-button icon-outlined-button hover:bg-green-bright-50 focus:bg-green-bright-50"
class={status_button_classes(:green)}
aria-label="go to last evaluated cell"
data-el-focus-cell-button
data-target={@cell_id}
>
<.remix_icon icon="loader-3-line" class="text-xl text-green-bright-400" />
<.remix_icon icon="loader-3-line" />
</button>
</span>
"""
@ -1070,12 +1061,12 @@ defmodule LivebookWeb.SessionLive.Render do
~H"""
<span class="tooltip left" data-tooltip="Go to last evaluated cell">
<button
class="border-red-300 icon-button icon-outlined-button hover:bg-red-50 focus:bg-red-50"
class={status_button_classes(:red)}
aria-label="go to last evaluated cell"
data-el-focus-cell-button
data-target={@cell_id}
>
<.remix_icon icon="loader-3-line" class="text-xl text-red-400" />
<.remix_icon icon="loader-3-line" />
</button>
</span>
"""
@ -1085,12 +1076,12 @@ defmodule LivebookWeb.SessionLive.Render do
~H"""
<span class="tooltip left" data-tooltip="Go to first stale cell">
<button
class="border-yellow-bright-200 icon-button icon-outlined-button hover:bg-yellow-bright-50 focus:bg-yellow-bright-50"
class={status_button_classes(:yellow)}
aria-label="go to first stale cell"
data-el-focus-cell-button
data-target={@cell_id}
>
<.remix_icon icon="loader-3-line" class="text-xl text-yellow-bright-300" />
<.remix_icon icon="loader-3-line" />
</button>
</span>
"""
@ -1099,16 +1090,35 @@ defmodule LivebookWeb.SessionLive.Render do
defp global_status(%{status: :fresh} = assigns) do
~H"""
<span class="tooltip left" data-tooltip="Ready to evaluate">
<div
class="border-gray-200 icon-button icon-outlined-button hover:bg-gray-100 focus:bg-gray-100"
aria-label="ready to evaluate"
>
<.remix_icon icon="loader-3-line" class="text-xl text-gray-400" />
<div class={status_button_classes(:gray)} aria-label="ready to evaluate">
<.remix_icon icon="loader-3-line" />
</div>
</span>
"""
end
defp status_button_classes(color) do
[
"text-xl leading-none p-1 flex items-center justify-center rounded-full rounded-full border-2",
case color do
:gray ->
"text-gray-400 border-gray-200 hover:bg-gray-100 focus:bg-gray-100"
:blue ->
"text-blue-500 border-blue-400 hover:bg-blue-50 focus:bg-blue-50"
:green ->
"text-green-bright-400 border-green-bright-300 hover:bg-green-bright-50 focus:bg-green-bright-50"
:yellow ->
"text-yellow-bright-300 border-yellow-bright-200 hover:bg-yellow-bright-50 focus:bg-yellow-bright-50"
:red ->
"text-red-400 border-red-300 hover:bg-red-50 focus:bg-red-50"
end
]
end
defp insert_mode_indicator(assigns) do
~H"""
<%!-- Note: this indicator is shown/hidden using CSS based on the current mode --%>
@ -1199,9 +1209,9 @@ defmodule LivebookWeb.SessionLive.Render do
</div>
<div class="mt-8 flex flex-col w-full space-y-16" data-el-sections-container>
<div :if={@data_view.section_views == []} class="flex justify-center">
<button class="button-base button-small" phx-click="append_section">
<LivebookWeb.SessionLive.InsertButtonsComponent.insert_button phx-click="append_section">
+ Section
</button>
</LivebookWeb.SessionLive.InsertButtonsComponent.insert_button>
</div>
<.live_component
:for={{section_view, index} <- Enum.with_index(@data_view.section_views)}
@ -1228,9 +1238,9 @@ defmodule LivebookWeb.SessionLive.Render do
defp star_button(%{file: nil} = assigns) do
~H"""
<span class="tooltip left" data-tooltip="Save this notebook before starring it">
<button class="icon-button" disabled>
<.remix_icon icon="star-line text-lg" />
</button>
<.icon_button disabled>
<.remix_icon icon="star-line" />
</.icon_button>
</span>
"""
end
@ -1239,15 +1249,15 @@ defmodule LivebookWeb.SessionLive.Render do
~H"""
<%= if @file in @starred_files do %>
<span class="tooltip left" data-tooltip="Unstar notebook">
<button class="icon-button" phx-click="unstar_notebook">
<.remix_icon icon="star-fill text-lg text-yellow-600" />
</button>
<.icon_button phx-click="unstar_notebook">
<.remix_icon icon="star-fill" class="text-yellow-600" />
</.icon_button>
</span>
<% else %>
<span class="tooltip left" data-tooltip="Star notebook">
<button class="icon-button" phx-click="star_notebook">
<.remix_icon icon="star-line text-lg" />
</button>
<.icon_button phx-click="star_notebook">
<.remix_icon icon="star-line" />
</.icon_button>
</span>
<% end %>
"""

View file

@ -126,13 +126,13 @@ defmodule LivebookWeb.SessionLive.SecretsComponent do
]}
/>
<div class="flex space-x-2">
<button class="button-base button-blue" type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon="add-line" class="align-middle" />
<.button type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon="add-line" />
<span class="font-normal">Add</span>
</button>
<.link patch={@return_to} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={@return_to}>
Cancel
</.link>
</.button>
</div>
</div>
</.form>
@ -198,15 +198,15 @@ defmodule LivebookWeb.SessionLive.SecretsComponent do
in <%= hub_label(@hub) %>. Allow this session to access it?
</span>
</div>
<button
class="button-base button-gray"
<.button
color="gray"
phx-click="select_secret"
phx-value-name={@secret.name}
phx-value-hub={true}
phx-target={@target}
>
Grant access
</button>
</.button>
</div>
</div>
</div>

View file

@ -87,16 +87,17 @@ defmodule LivebookWeb.SessionLive.SecretsListComponent do
<span class="text-sm font-mono break-all">
*****
</span>
<button
<.icon_button
small
id={"session-secret-#{@secret.name}-copy"}
type="button"
phx-click={JS.dispatch("lb:clipcopy", detail: %{content: @secret.value})}
class="icon-button"
>
<.remix_icon icon="clipboard-line" />
</button>
</.icon_button>
</div>
<button
<.icon_button
small
id={"session-secret-#{@secret.name}-delete"}
type="button"
phx-click={
@ -105,10 +106,9 @@ defmodule LivebookWeb.SessionLive.SecretsListComponent do
target: @myself
)
}
class="icon-button"
>
<.remix_icon icon="delete-bin-line" />
</button>
</.icon_button>
</div>
</div>
"""
@ -138,16 +138,15 @@ defmodule LivebookWeb.SessionLive.SecretsListComponent do
'''
}
>
<button
<.icon_button
:if={Session.Data.secret_outdated?(@secret, @secrets)}
class="icon-button"
aria-label="load latest value"
phx-click={
JS.push("update_outdated", value: %{"name" => @secret.name}, target: @myself)
}
>
<.remix_icon icon="refresh-line" class="text-xl leading-none" />
</button>
<.remix_icon icon="refresh-line" />
</.icon_button>
</span>
<.form
:let={f}
@ -166,23 +165,23 @@ defmodule LivebookWeb.SessionLive.SecretsListComponent do
<span class="text-sm font-mono break-all">
*****
</span>
<button
<.icon_button
small
id={"#{@id}-copy-button"}
type="button"
phx-click={JS.dispatch("lb:clipcopy", detail: %{content: @secret.value})}
class="icon-button"
>
<.remix_icon icon="clipboard-line" />
</button>
</.icon_button>
</div>
<.link
<.icon_button
small
id={"#{@id}-edit-button"}
navigate={~p"/hub/#{@secret.hub_id}/secrets/edit/#{@secret.name}"}
class="icon-button"
role="button"
>
<.remix_icon icon="pencil-line" />
</.link>
</.icon_button>
</div>
</div>
</div>
@ -193,7 +192,7 @@ defmodule LivebookWeb.SessionLive.SecretsListComponent do
defp secrets_info_icon(assigns) do
~H"""
<span
class="icon-button p-0 cursor-pointer tooltip bottom-left"
class="tooltip bottom-left"
data-tooltip={
~S'''
Secrets are a safe way to share credentials
@ -203,7 +202,9 @@ defmodule LivebookWeb.SessionLive.SecretsListComponent do
'''
}
>
<.remix_icon icon="question-line" class="text-xl leading-none" />
<.icon_button>
<.remix_icon icon="question-line" />
</.icon_button>
</span>
"""
end

View file

@ -17,8 +17,7 @@ defmodule LivebookWeb.SessionLive.SectionComponent do
data-p-metadata={hook_prop(@section_view.id)}
>
<div class="absolute left-0 top-0 bottom-0 transform -translate-x-full w-10 flex justify-end items-center pr-2">
<button
class="icon-button"
<.icon_button
aria-label="collapse section"
data-el-section-collapse-button
phx-click={
@ -27,10 +26,9 @@ defmodule LivebookWeb.SessionLive.SectionComponent do
)
}
>
<.remix_icon icon="arrow-down-s-line" class="text-xl" />
</button>
<button
class="icon-button"
<.remix_icon icon="arrow-down-s-line" />
</.icon_button>
<.icon_button
aria-label="expand section"
data-el-section-expand-button
phx-click={
@ -39,8 +37,8 @@ defmodule LivebookWeb.SessionLive.SectionComponent do
)
}
>
<.remix_icon icon="arrow-right-s-line" class="text-xl" />
</button>
<.remix_icon icon="arrow-right-s-line" />
</.icon_button>
</div>
<h2
class="grow text-gray-800 font-semibold text-2xl px-1 -ml-1.5 rounded-lg border border-transparent whitespace-pre-wrap cursor-text scroll-mt-[50px] sm:scroll-mt-0"
@ -66,52 +64,50 @@ defmodule LivebookWeb.SessionLive.SectionComponent do
class="tooltip top"
data-tooltip={cannot_branch_out_reason(@section_view) || "Branch out from"}
>
<button
class={["icon-button", cannot_branch_out_reason(@section_view) && "disabled"]}
<.icon_button
disabled={cannot_branch_out_reason(@section_view)}
aria-label="branch out from other section"
>
<.remix_icon icon="git-branch-line" class="text-xl flip-horizontally" />
</button>
<.remix_icon icon="git-branch-line" class="flip-horizontally" />
</.icon_button>
</span>
</.branching_menu>
<span class="tooltip top" data-tooltip="Link">
<a href={"##{@section_view.html_id}"} class="icon-button" aria-label="link to section">
<.remix_icon icon="link" class="text-xl" />
</a>
<.icon_button href={"##{@section_view.html_id}"} aria-label="link to section">
<.remix_icon icon="link" />
</.icon_button>
</span>
<span class="tooltip top" data-tooltip="Move up">
<button
class="icon-button"
<.icon_button
aria-label="move section up"
phx-click="move_section"
phx-value-section_id={@section_view.id}
phx-value-offset="-1"
>
<.remix_icon icon="arrow-up-s-line" class="text-xl" />
</button>
<.remix_icon icon="arrow-up-s-line" />
</.icon_button>
</span>
<span class="tooltip top" data-tooltip="Move down">
<button
class="icon-button"
<.icon_button
aria-label="move section down"
phx-click="move_section"
phx-value-section_id={@section_view.id}
phx-value-offset="1"
>
<.remix_icon icon="arrow-down-s-line" class="text-xl" />
</button>
<.remix_icon icon="arrow-down-s-line" />
</.icon_button>
</span>
<span {if @section_view.has_children?,
do: [class: "tooltip left", "data-tooltip": "Cannot delete this section because\nother sections branch from it"],
else: [class: "tooltip top", "data-tooltip": "Delete"]}>
<button
class={["icon-button", @section_view.has_children? && "disabled"]}
<.icon_button
disabled={@section_view.has_children?}
aria-label="delete section"
phx-click="delete_section"
phx-value-section_id={@section_view.id}
>
<.remix_icon icon="delete-bin-6-line" class="text-xl" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
</div>
</div>

View file

@ -68,10 +68,10 @@ defmodule LivebookWeb.SettingsLive do
</div>
<div class="self-center">
<.link navigate={~p"/dashboard"} class="button-base button-outlined-gray">
<.remix_icon icon="dashboard-2-line" class="align-middle mr-1" />
<.button navigate={~p"/dashboard"} color="gray" outlined>
<.remix_icon icon="dashboard-2-line" />
<span>Open dashboard</span>
</.link>
</.button>
</div>
</div>
</div>
@ -210,20 +210,19 @@ defmodule LivebookWeb.SettingsLive do
file_system_select_disabled={true}
target={self()}
>
<button class="button-base button-gray" phx-click="cancel_autosave_path" tabindex="-1">
<.button color="gray" phx-click="cancel_autosave_path" tabindex="-1">
Cancel
</button>
<button class="button-base button-gray" phx-click="reset_autosave_path" tabindex="-1">
</.button>
<.button color="gray" phx-click="reset_autosave_path" tabindex="-1">
Reset
</button>
<button
class="button-base button-blue"
</.button>
<.button
phx-click="set_autosave_path"
disabled={not Livebook.FileSystem.File.dir?(@state.file)}
tabindex="-1"
>
Save
</button>
</.button>
</.live_component>
</div>
"""
@ -231,11 +230,13 @@ defmodule LivebookWeb.SettingsLive do
defp autosave_path_select(assigns) do
~H"""
<div class="flex">
<input class="input mr-2" readonly value={@state.file.path} />
<button class="button-base button-gray" phx-click="open_autosave_path_select">
<div class="flex gap-2">
<div class="grow">
<.text_field name={nil} readonly value={@state.file.path} />
</div>
<.button color="gray" phx-click="open_autosave_path_select">
Change
</button>
</.button>
</div>
"""
end

View file

@ -50,17 +50,12 @@ defmodule LivebookWeb.SettingsLive.EnvVarComponent do
<.text_field field={f[:value]} label="Value" autofocus={@operation == :edit} />
<.hidden_field field={f[:operation]} value={@operation} />
<div class="flex space-x-2">
<button
class="button-base button-blue"
type="submit"
phx-disable-with="Adding..."
disabled={not @changeset.valid?}
>
<.button type="submit" phx-disable-with="Adding..." disabled={not @changeset.valid?}>
Save
</button>
<.link patch={@return_to} class="button-base button-outlined-gray">
</.button>
<.button color="gray" outlined patch={@return_to}>
Cancel
</.link>
</.button>
</div>
</div>
</.form>

View file

@ -19,39 +19,37 @@ defmodule LivebookWeb.SettingsLive.EnvVarsComponent do
<:col :let={env_var} label="Name"><%= env_var.name %></:col>
<:action :let={env_var}>
<span class="tooltip left" data-tooltip="Edit">
<button
<.icon_button
id={"env-var-#{env_var.name}-edit"}
type="button"
phx-click={
JS.push("edit_env_var", value: %{env_var: env_var.name}, target: @target)
}
class="icon-button"
>
<.remix_icon icon="edit-fill" class="text-lg" />
</button>
<.remix_icon icon="edit-fill" />
</.icon_button>
</span>
</:action>
<:action :let={env_var}>
<span class="tooltip left" data-tooltip="Delete">
<button
<.icon_button
id={"env-var-#{env_var.name}-delete"}
type="button"
phx-click={
JS.push("delete_env_var", value: %{env_var: env_var.name}, target: @target)
}
class="icon-button"
>
<.remix_icon icon="delete-bin-6-line" class="text-lg" />
</button>
<.remix_icon icon="delete-bin-6-line" />
</.icon_button>
</span>
</:action>
</.table>
</div>
</div>
<div class="flex">
<.link patch={@add_env_var_path} class="button-base button-blue" id="add-env-var">
<.button patch={@add_env_var_path} id="add-env-var">
Add environment variable
</.link>
</.button>
</div>
</div>
"""

View file

@ -49,14 +49,10 @@ defmodule LivebookWeb.UserComponent do
label="Cursor color"
randomize={JS.push("randomize_color", target: @myself)}
/>
<button
class="button-base button-blue flex space-x-1 justify-center items-center"
type="submit"
disabled={not @changeset.valid?}
>
<.button type="submit" disabled={not @changeset.valid?}>
<.remix_icon icon="save-line" />
<span>Save</span>
</button>
</.button>
</div>
</.form>
</div>