mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-04 12:04:20 +08:00
Refactor modals with JS commands (#669)
* Use JS commands for closing the modal with animations * Refactor modal to render content as slot * Bump LV
This commit is contained in:
parent
3afa81f454
commit
ad4867ddfb
12 changed files with 194 additions and 175 deletions
|
@ -30,4 +30,32 @@
|
|||
.scroll-smooth {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
.fade-in {
|
||||
animation: fade-in-frames 200ms;
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
animation: fade-out-frames 200ms;
|
||||
}
|
||||
|
||||
@keyframes fade-in-frames {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-out-frames {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,8 +393,8 @@ function handleDocumentMouseDown(hook, event) {
|
|||
return;
|
||||
}
|
||||
|
||||
// If primary cell action is clicked, keep the focus as is
|
||||
if (event.target.closest(`[data-element="actions"][data-primary]`)) {
|
||||
// If a cell action is clicked, keep the focus as is
|
||||
if (event.target.closest(`[data-element="actions"]`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
6
assets/package-lock.json
generated
6
assets/package-lock.json
generated
|
@ -54,14 +54,14 @@
|
|||
}
|
||||
},
|
||||
"../deps/phoenix": {
|
||||
"version": "1.5.12",
|
||||
"version": "1.6.2",
|
||||
"license": "MIT"
|
||||
},
|
||||
"../deps/phoenix_html": {
|
||||
"version": "3.0.0"
|
||||
"version": "3.1.0"
|
||||
},
|
||||
"../deps/phoenix_live_view": {
|
||||
"version": "0.16.0",
|
||||
"version": "0.17.4",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
|
|
|
@ -62,6 +62,7 @@ defmodule LivebookWeb do
|
|||
|
||||
# Import basic rendering functionality (render, render_layout, etc)
|
||||
import Phoenix.View
|
||||
alias Phoenix.LiveView.JS
|
||||
alias LivebookWeb.Router.Helpers, as: Routes
|
||||
|
||||
# Custom helpers
|
||||
|
|
|
@ -1,44 +1,60 @@
|
|||
defmodule LivebookWeb.Helpers do
|
||||
use Phoenix.Component
|
||||
|
||||
alias Phoenix.LiveView.JS
|
||||
|
||||
alias LivebookWeb.Router.Helpers, as: Routes
|
||||
|
||||
alias Livebook.FileSystem
|
||||
|
||||
@doc """
|
||||
Renders a component inside the `Livebook.ModalComponent` component.
|
||||
Wraps the given content in a modal dialog.
|
||||
|
||||
The rendered modal receives a `:return_to` option to properly update
|
||||
the URL when the modal is closed.
|
||||
When closed, the modal redirects to the given `:return_to` URL.
|
||||
|
||||
## Example
|
||||
|
||||
<.live_modal return_to={...}>
|
||||
<.live_component module={MyComponent} />
|
||||
</.live_modal>
|
||||
"""
|
||||
def live_modal(component, opts) do
|
||||
{modal_opts, opts} = build_modal_opts(opts)
|
||||
modal_opts = [{:render_spec, {:component, component, opts}} | modal_opts]
|
||||
live_component(LivebookWeb.ModalComponent, modal_opts)
|
||||
def modal(assigns) do
|
||||
assigns =
|
||||
assigns
|
||||
|> assign_new(:class, fn -> "" end)
|
||||
|
||||
~H"""
|
||||
<div class="fixed z-[10000] inset-0 fade-in" phx-remove={JS.transition("fade-out")}>
|
||||
<!-- Modal container -->
|
||||
<div class="h-screen flex items-center justify-center p-4">
|
||||
<!-- Overlay -->
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75 z-0" aria-hidden="true"></div>
|
||||
|
||||
<!-- Modal box -->
|
||||
<div class={"relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl #{@class}"}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
phx-window-keydown={click_modal_close()}
|
||||
phx-click-away={click_modal_close()}
|
||||
phx-key="escape">
|
||||
|
||||
<%= live_patch to: @return_to,
|
||||
class: "absolute top-6 right-6 text-gray-400 flex space-x-1 items-center",
|
||||
aria_label: "close modal",
|
||||
id: "close-modal-button" do %>
|
||||
<span class="text-sm">(esc)</span>
|
||||
<.remix_icon icon="close-line" class="text-2xl" />
|
||||
<% end %>
|
||||
|
||||
<%= render_slot(@inner_block) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a live view inside the `Livebook.ModalComponent` component.
|
||||
|
||||
See `live_modal/2` for more details.
|
||||
"""
|
||||
def live_modal(socket, live_view, opts) do
|
||||
{modal_opts, opts} = build_modal_opts(opts)
|
||||
modal_opts = [{:render_spec, {:live_view, socket, live_view, opts}} | modal_opts]
|
||||
live_component(LivebookWeb.ModalComponent, modal_opts)
|
||||
end
|
||||
|
||||
defp build_modal_opts(opts) do
|
||||
path = Keyword.fetch!(opts, :return_to)
|
||||
{modal_class, opts} = Keyword.pop(opts, :modal_class)
|
||||
|
||||
modal_opts = [
|
||||
id: "modal",
|
||||
return_to: path,
|
||||
modal_class: modal_class
|
||||
]
|
||||
|
||||
{modal_opts, opts}
|
||||
defp click_modal_close(js \\ %JS{}) do
|
||||
JS.dispatch(js, "click", to: "#close-modal-button")
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
@ -2,6 +2,7 @@ defmodule LivebookWeb.ExploreLive do
|
|||
use LivebookWeb, :live_view
|
||||
|
||||
import LivebookWeb.SessionHelpers
|
||||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias LivebookWeb.{SidebarHelpers, ExploreHelpers, PageHelpers}
|
||||
alias Livebook.Notebook.Explore
|
||||
|
@ -70,11 +71,9 @@ defmodule LivebookWeb.ExploreLive do
|
|||
</div>
|
||||
|
||||
<%= if @live_action == :user do %>
|
||||
<%= live_modal LivebookWeb.UserComponent,
|
||||
id: "user",
|
||||
modal_class: "w-full max-w-sm",
|
||||
user: @current_user,
|
||||
return_to: Routes.explore_path(@socket, :page) %>
|
||||
<.current_user_modal
|
||||
return_to={Routes.explore_path(@socket, :page)}
|
||||
current_user={@current_user} />
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@ defmodule LivebookWeb.HomeLive do
|
|||
use LivebookWeb, :live_view
|
||||
|
||||
import LivebookWeb.SessionHelpers
|
||||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias LivebookWeb.{SidebarHelpers, ExploreHelpers}
|
||||
alias Livebook.{Sessions, Session, LiveMarkdown, Notebook, FileSystem}
|
||||
|
@ -115,28 +116,27 @@ defmodule LivebookWeb.HomeLive do
|
|||
</div>
|
||||
|
||||
<%= if @live_action == :user do %>
|
||||
<%= live_modal LivebookWeb.UserComponent,
|
||||
id: "user",
|
||||
modal_class: "w-full max-w-sm",
|
||||
user: @current_user,
|
||||
return_to: Routes.home_path(@socket, :page) %>
|
||||
<.current_user_modal
|
||||
return_to={Routes.home_path(@socket, :page)}
|
||||
current_user={@current_user} />
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :close_session do %>
|
||||
<%= live_modal LivebookWeb.HomeLive.CloseSessionComponent,
|
||||
id: "close-session",
|
||||
modal_class: "w-full max-w-xl",
|
||||
return_to: Routes.home_path(@socket, :page),
|
||||
session: @session %>
|
||||
<.modal class="w-full max-w-xl" return_to={Routes.home_path(@socket, :page)}>
|
||||
<.live_component module={LivebookWeb.HomeLive.CloseSessionComponent}
|
||||
id="close-session"
|
||||
return_to={Routes.home_path(@socket, :page)}
|
||||
session={@session} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :import do %>
|
||||
<%= live_modal LivebookWeb.HomeLive.ImportComponent,
|
||||
id: "import",
|
||||
modal_class: "w-full max-w-xl",
|
||||
return_to: Routes.home_path(@socket, :page),
|
||||
tab: @tab,
|
||||
import_opts: @import_opts %>
|
||||
<.modal class="w-full max-w-xl" return_to={Routes.home_path(@socket, :page)}>
|
||||
<.live_component module={LivebookWeb.HomeLive.ImportComponent}
|
||||
id="import"
|
||||
tab={@tab}
|
||||
import_opts={@import_opts} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
defmodule LivebookWeb.ModalComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="fixed z-[10000] inset-0">
|
||||
|
||||
<!-- Modal container -->
|
||||
<div class="h-screen flex items-center justify-center p-4">
|
||||
<!-- Overlay -->
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75 z-0"
|
||||
aria-hidden="true"
|
||||
phx-window-keydown="close"
|
||||
phx-key="escape"
|
||||
phx-target={@myself}
|
||||
phx-page-loading></div>
|
||||
|
||||
<!-- Modal box -->
|
||||
<div class={"relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl #{@modal_class}"}
|
||||
phx-click-away="close"
|
||||
phx-target={@myself}
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
|
||||
<%= live_patch to: @return_to, class: "absolute top-6 right-6 text-gray-400 flex space-x-1 items-center" do %>
|
||||
<span class="text-sm">(esc)</span>
|
||||
<.remix_icon icon="close-line" class="text-2xl" />
|
||||
<% end %>
|
||||
|
||||
<%=
|
||||
case @render_spec do
|
||||
{:component, component, opts} -> live_component(component, opts)
|
||||
{:live_view, socket, live_view, opts} -> live_render(socket, live_view, opts)
|
||||
end
|
||||
%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("close", _params, socket) do
|
||||
{:noreply, push_patch(socket, to: socket.assigns.return_to)}
|
||||
end
|
||||
end
|
|
@ -267,88 +267,90 @@ defmodule LivebookWeb.SessionLive do
|
|||
</div>
|
||||
|
||||
<%= if @live_action == :user do %>
|
||||
<%= live_modal LivebookWeb.UserComponent,
|
||||
id: "user",
|
||||
modal_class: "w-full max-w-sm",
|
||||
user: @current_user,
|
||||
return_to: Routes.session_path(@socket, :page, @session.id) %>
|
||||
<.current_user_modal
|
||||
return_to={Routes.session_path(@socket, :page, @session.id)}
|
||||
current_user={@current_user} />
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :runtime_settings do %>
|
||||
<%= live_modal LivebookWeb.SessionLive.RuntimeComponent,
|
||||
id: "runtime-settings",
|
||||
modal_class: "w-full max-w-4xl",
|
||||
return_to: Routes.session_path(@socket, :page, @session.id),
|
||||
session: @session,
|
||||
runtime: @data_view.runtime %>
|
||||
<.modal class="w-full max-w-4xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<.live_component module={LivebookWeb.SessionLive.RuntimeComponent}
|
||||
id="runtime-settings"
|
||||
session={@session}
|
||||
runtime={@data_view.runtime} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :file_settings do %>
|
||||
<%= live_modal @socket, LivebookWeb.SessionLive.PersistenceLive,
|
||||
id: "persistence",
|
||||
modal_class: "w-full max-w-4xl",
|
||||
return_to: Routes.session_path(@socket, :page, @session.id),
|
||||
session: %{
|
||||
"session" => @session,
|
||||
"file" => @data_view.file,
|
||||
"persist_outputs" => @data_view.persist_outputs,
|
||||
"autosave_interval_s" => @data_view.autosave_interval_s
|
||||
} %>
|
||||
<.modal class="w-full max-w-4xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<%= live_render @socket, LivebookWeb.SessionLive.PersistenceLive,
|
||||
id: "persistence",
|
||||
session: %{
|
||||
"session" => @session,
|
||||
"file" => @data_view.file,
|
||||
"persist_outputs" => @data_view.persist_outputs,
|
||||
"autosave_interval_s" => @data_view.autosave_interval_s
|
||||
} %>
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :shortcuts do %>
|
||||
<%= live_modal LivebookWeb.SessionLive.ShortcutsComponent,
|
||||
id: "shortcuts",
|
||||
modal_class: "w-full max-w-6xl",
|
||||
platform: @platform,
|
||||
return_to: Routes.session_path(@socket, :page, @session.id) %>
|
||||
<.modal class="w-full max-w-6xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<.live_component module={LivebookWeb.SessionLive.ShortcutsComponent}
|
||||
id="shortcuts"
|
||||
platform={@platform} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :cell_settings do %>
|
||||
<%= live_modal settings_component_for(@cell),
|
||||
id: "cell-settings",
|
||||
modal_class: "w-full max-w-xl",
|
||||
session: @session,
|
||||
cell: @cell,
|
||||
return_to: Routes.session_path(@socket, :page, @session.id) %>
|
||||
<.modal class="w-full max-w-xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<.live_component module={settings_component_for(@cell)}
|
||||
id="cell-settings"
|
||||
session={@session}
|
||||
return_to={Routes.session_path(@socket, :page, @session.id)}
|
||||
cell={@cell} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :cell_upload do %>
|
||||
<%= live_modal LivebookWeb.SessionLive.CellUploadComponent,
|
||||
id: "cell-upload",
|
||||
modal_class: "w-full max-w-xl",
|
||||
session: @session,
|
||||
cell: @cell,
|
||||
uploads: @uploads,
|
||||
return_to: Routes.session_path(@socket, :page, @session.id) %>
|
||||
<.modal class="w-full max-w-xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<.live_component module={LivebookWeb.SessionLive.CellUploadComponent}
|
||||
id="cell-upload"
|
||||
session={@session}
|
||||
return_to={Routes.session_path(@socket, :page, @session.id)}
|
||||
cell={@cell}
|
||||
uploads={@uploads} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :delete_section do %>
|
||||
<%= live_modal LivebookWeb.SessionLive.DeleteSectionComponent,
|
||||
id: "delete-section",
|
||||
modal_class: "w-full max-w-xl",
|
||||
session: @session,
|
||||
section: @section,
|
||||
is_first: @section.id == @first_section_id,
|
||||
return_to: Routes.session_path(@socket, :page, @session.id) %>
|
||||
<.modal class="w-full max-w-xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<.live_component module={LivebookWeb.SessionLive.DeleteSectionComponent}
|
||||
id="delete-section"
|
||||
session={@session}
|
||||
return_to={Routes.session_path(@socket, :page, @session.id)}
|
||||
section={@section}
|
||||
is_first={@section.id == @first_section_id} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :bin do %>
|
||||
<%= live_modal LivebookWeb.SessionLive.BinComponent,
|
||||
id: "bin",
|
||||
modal_class: "w-full max-w-4xl",
|
||||
session: @session,
|
||||
bin_entries: @data_view.bin_entries,
|
||||
return_to: Routes.session_path(@socket, :page, @session.id) %>
|
||||
<.modal class="w-full max-w-4xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<.live_component module={LivebookWeb.SessionLive.BinComponent}
|
||||
id="bin"
|
||||
session={@session}
|
||||
return_to={Routes.session_path(@socket, :page, @session.id)}
|
||||
bin_entries={@data_view.bin_entries} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :export do %>
|
||||
<%= live_modal LivebookWeb.SessionLive.ExportComponent,
|
||||
id: "export",
|
||||
modal_class: "w-full max-w-4xl",
|
||||
session: @session,
|
||||
tab: @tab,
|
||||
return_to: Routes.session_path(@socket, :page, @session.id) %>
|
||||
<.modal class="w-full max-w-4xl" return_to={Routes.session_path(@socket, :page, @session.id)}>
|
||||
<.live_component module={LivebookWeb.SessionLive.ExportComponent}
|
||||
id="export"
|
||||
session={@session}
|
||||
tab={@tab} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
defmodule LivebookWeb.SettingsLive do
|
||||
use LivebookWeb, :live_view
|
||||
|
||||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias LivebookWeb.{SidebarHelpers, PageHelpers}
|
||||
|
||||
@impl true
|
||||
|
@ -66,26 +68,26 @@ defmodule LivebookWeb.SettingsLive do
|
|||
</div>
|
||||
|
||||
<%= if @live_action == :user do %>
|
||||
<%= live_modal LivebookWeb.UserComponent,
|
||||
id: "user",
|
||||
modal_class: "w-full max-w-sm",
|
||||
user: @current_user,
|
||||
return_to: Routes.settings_path(@socket, :page) %>
|
||||
<.current_user_modal
|
||||
return_to={Routes.settings_path(@socket, :page)}
|
||||
current_user={@current_user} />
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :add_file_system do %>
|
||||
<%= live_modal LivebookWeb.SettingsLive.AddFileSystemComponent,
|
||||
id: "add-file-system",
|
||||
modal_class: "w-full max-w-3xl",
|
||||
return_to: Routes.settings_path(@socket, :page) %>
|
||||
<.modal class="w-full max-w-3xl" return_to={Routes.settings_path(@socket, :page)}>
|
||||
<.live_component module={LivebookWeb.SettingsLive.AddFileSystemComponent}
|
||||
id="add-file-system"
|
||||
return_to={Routes.settings_path(@socket, :page)} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<%= if @live_action == :detach_file_system do %>
|
||||
<%= live_modal LivebookWeb.SettingsLive.RemoveFileSystemComponent,
|
||||
id: "detach-file-system",
|
||||
modal_class: "w-full max-w-xl",
|
||||
file_system: @file_system,
|
||||
return_to: Routes.settings_path(@socket, :page) %>
|
||||
<.modal class="w-full max-w-xl" return_to={Routes.settings_path(@socket, :page)}>
|
||||
<.live_component module={LivebookWeb.SettingsLive.RemoveFileSystemComponent}
|
||||
id="detach-file-system"
|
||||
return_to={Routes.settings_path(@socket, :page)}
|
||||
file_system={@file_system} />
|
||||
</.modal>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
|
|
@ -37,4 +37,22 @@ defmodule LivebookWeb.UserHelpers do
|
|||
initials -> List.first(initials) <> List.last(initials)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders the current user edit form in a modal.
|
||||
|
||||
## Examples
|
||||
|
||||
<.current_user_modal return_to={...} current_user={@current_user} />
|
||||
"""
|
||||
def current_user_modal(assigns) do
|
||||
~H"""
|
||||
<LivebookWeb.Helpers.modal class="w-full max-w-sm" return_to={@return_to}>
|
||||
<.live_component module={LivebookWeb.UserComponent}
|
||||
id="user"
|
||||
return_to={@return_to}
|
||||
user={@current_user} />
|
||||
</LivebookWeb.Helpers.modal>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -15,7 +15,7 @@
|
|||
"phoenix_html": {:hex, :phoenix_html, "3.1.0", "0b499df05aad27160d697a9362f0e89fa0e24d3c7a9065c2bd9d38b4d1416c09", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0c0a98a2cefa63433657983a2a594c7dee5927e4391e0f1bfd3a151d1def33fc"},
|
||||
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.1", "fb94a33c077141f9ac7930b322a7a3b99f9b144bf3a08dd667b9f9aaf0319889", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "6faf1373e5846c8ab68c2cf55cfa5c196c1fbbe0c72d12a4cdfaaac6ef189948"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "0.17.4", "c994c248c1195ccdb25a4e990eadabddb34c59d8f620d5c0aec418e2b7913651", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "86d27af55e78bd42476a4084d54c3add660e565c07233d8afacb20ab6d7f53b6"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "0.17.5", "63f52a6f9f6983f04e424586ff897c016ecc5e4f8d1e2c22c2887af1c57215d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c5586e6a3d4df71b8214c769d4f5eb8ece2b4001711a7ca0f97323c36958b0e3"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
|
||||
"phoenix_view": {:hex, :phoenix_view, "1.0.0", "fea71ecaaed71178b26dd65c401607de5ec22e2e9ef141389c721b3f3d4d8011", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "82be3e2516f5633220246e2e58181282c71640dab7afc04f70ad94253025db0c"},
|
||||
"plug": {:hex, :plug, "1.12.1", "645678c800601d8d9f27ad1aebba1fdb9ce5b2623ddb961a074da0b96c35187d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d57e799a777bc20494b784966dc5fbda91eb4a09f571f76545b72a634ce0d30b"},
|
||||
|
|
Loading…
Add table
Reference in a new issue