Skip to content

Commit

Permalink
Improve escape and focus
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurClemens committed Aug 17, 2024
1 parent 6b324a4 commit 89e735d
Show file tree
Hide file tree
Showing 23 changed files with 1,861 additions and 236 deletions.
15 changes: 6 additions & 9 deletions lib/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11487,6 +11487,7 @@ defmodule PrimerLive.Component do

assigns =
assigns
|> assign(:is_show, assigns.is_show || assigns.is_show_on_mount)
|> assign(:form, form)
|> assign(:field, field)
|> assign(:checkbox_attrs, checkbox_attrs)
Expand All @@ -11510,7 +11511,7 @@ defmodule PrimerLive.Component do
<div {@touch_layer_attrs} phx-click={not @is_modal && cancel_dialog("##{@id}")}></div>
<.focus_wrap
id={@focus_wrap_id}
phx-window-keydown={@is_escapable && JS.exec("data-cancel", to: "##{@id}")}
phx-keydown={@is_escapable && JS.exec("data-cancel", to: "##{@id}")}
phx-key="escape"
>
<.box {@box_attrs}>
Expand All @@ -11529,9 +11530,9 @@ defmodule PrimerLive.Component do
"""
end

defp on_mount_dialog(js, selector) do
js
|> show_dialog(selector)
defp on_mount_dialog(selector, transition_duration) do
%JS{}
|> show_dialog(selector, transition_duration)
end

defp on_remove_dialog(
Expand Down Expand Up @@ -11563,8 +11564,6 @@ defmodule PrimerLive.Component do
selector,
transition_duration
) do
dbg({"show_dialog", js, selector, transition_duration})

js
|> JS.show(
to: selector,
Expand All @@ -11575,7 +11574,6 @@ defmodule PrimerLive.Component do
to: selector,
detail: %{action: "show", transitionDuration: transition_duration}
)

# Persist across routes and form updates:
|> JS.set_attribute({"data-isopen", ""}, to: selector)
end
Expand All @@ -11596,13 +11594,12 @@ defmodule PrimerLive.Component do
selector,
transition_duration
) do
dbg({"hide_dialog", js, selector, transition_duration})

js
|> JS.dispatch("prompt:toggle",
to: selector,
detail: %{action: "hide", transitionDuration: transition_duration}
)
# Wait before removing "data-isopen"
|> JS.transition(
{"duration-#{transition_duration}", "", ""},
time: transition_duration,
Expand Down
5 changes: 3 additions & 2 deletions lib/helpers/attribute_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1045,8 +1045,9 @@ defmodule PrimerLive.Helpers.AttributeHelpers do
assigns[:is_fast] && ["data-isfast": ""],
# Dialog and drawer specific:
is_modal && ["data-ismodal": ""],
assigns[:is_escapable] && ["data-isescapable": ""],
assigns[:focus_first] && ["data-focusfirst": assigns[:focus_first]]
not is_nil(assigns[:is_escapable]) && ["data-isescapable": assigns[:is_escapable] |> to_string()],
assigns[:focus_first] && ["data-focusfirst": assigns[:focus_first]],
assigns[:is_show_on_mount] && ["data-isopen": ""]
])

backdrop_attrs =
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/prompt_declaration_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ defmodule PrimerLive.Helpers.PromptDeclarationHelpers do
defmacro is_escapable do
quote do
attr :is_escapable, :boolean,
default: false,
default: true,
doc: """
Closes the content when pressing the Escape key.
Closes the content when the Escape key is pressed. Set to false to prevent this.
"""
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/primer_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ defmodule PrimerLive do
quote do
import PrimerLive.Component
import PrimerLive.Octicons
import PrimerLive.StatefulConditionComponent
alias PrimerLive.Theme
end
end
Expand Down
68 changes: 68 additions & 0 deletions lib/stateful_condition_component.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
defmodule PrimerLive.StatefulConditionComponent do
@moduledoc """
A wrapper component that takes a condition and compares the initial state with the current state after a re-render.
This is useful when the wrapped component should behave differently on initial mount and subsequent updates.
## Examples
In this example, the 'Create new post' dialog is shown when `@live_action` is `:new`.
When triggered from the posts listing, the dialog appears with a fade-in effect.
If the page is reloaded while still on that route, the dialog is shown immediately
using the dialog attribute `is_show_on_mount`.
<.live_component
id="create-post-dialog-component"
module={PrimerLive.StatefulConditionComponent}
condition={@live_action == :new || nil}
:let={
%{
equals_initial_condition: equals_initial_condition,
condition: condition
}
}
>
<.dialog id="create-post"
:if={condition}
is_show
is_show_on_mount={equals_initial_condition}
on_cancel={JS.patch(~p"/posts")}
>
...
</.dialog>
</.live_component>
"""
use Phoenix.LiveComponent

@impl true
def render(assigns) do
~H"""
<div>
<%= render_slot(@inner_block, %{
equals_initial_condition: @equals_initial_condition,
condition: @condition
}) %>
</div>
"""
end

@impl true
def update(assigns, socket) do
initial_condition = get_initial_condition(assigns, socket)

socket =
socket
|> assign(assigns)
|> assign(:initial_condition, initial_condition)
|> assign(:equals_initial_condition, initial_condition == assigns[:condition])

{:ok, socket}
end

defp get_initial_condition(%{condition: condition} = _assigns, _socket)
when is_nil(condition),
do: :unset

defp get_initial_condition(assigns, socket) do
socket.assigns[:initial_condition] || assigns[:condition] || :unset
end
end
Loading

0 comments on commit 89e735d

Please sign in to comment.