Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add blocking: true to JS.dispatch #3615

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion assets/js/phoenix_live_view/js.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,15 @@ let JS = {
})
},

exec_dispatch(e, eventType, phxEvent, view, sourceEl, el, {event, detail, bubbles}){
exec_dispatch(e, eventType, phxEvent, view, sourceEl, el, {event, detail, bubbles, blocking}){
detail = detail || {}
detail.dispatcher = sourceEl
if(blocking){
const promise = new Promise((resolve, _reject) => {
detail.done = resolve
})
liveSocket.asyncTransition(promise)
}
DOM.dispatchEvent(el, event, {detail, bubbles})
},

Expand Down
16 changes: 15 additions & 1 deletion assets/js/phoenix_live_view/live_socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ export default class LiveSocket {
this.transitions.after(callback)
}

asyncTransition(promise){
this.transitions.addAsyncTransition(promise)
}

transition(time, onStart, onDone = function(){}){
this.transitions.addTransition(time, onStart, onDone)
}
Expand Down Expand Up @@ -993,6 +997,7 @@ export default class LiveSocket {
class TransitionSet {
constructor(){
this.transitions = new Set()
this.promises = new Set()
this.pendingOps = []
}

Expand All @@ -1001,6 +1006,7 @@ class TransitionSet {
clearTimeout(timer)
this.transitions.delete(timer)
})
this.promises.clear()
this.flushPendingOps()
}

Expand All @@ -1022,9 +1028,17 @@ class TransitionSet {
this.transitions.add(timer)
}

addAsyncTransition(promise){
this.promises.add(promise)
promise.then(() => {
this.promises.delete(promise)
this.flushPendingOps()
})
}

pushPendingOp(op){ this.pendingOps.push(op) }

size(){ return this.transitions.size }
size(){ return this.transitions.size + this.promises.size }

flushPendingOps(){
if(this.size() > 0){ return }
Expand Down
14 changes: 13 additions & 1 deletion lib/phoenix_live_view/js.ex
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ defmodule Phoenix.LiveView.JS do
with the client event. The details will be available in the
`event.detail` attribute for event listeners.
* `:bubbles` – A boolean flag to bubble the event or not. Defaults to `true`.
* `:blocking` - A boolean flag to block the UI until the event handler calls `event.detail.done()`.
The done function is injected by LiveView and *must* be called eventually to unblock the UI.
This is useful to integrate with third party JavaScript based animation libraries.

## Examples

Expand All @@ -245,7 +248,7 @@ defmodule Phoenix.LiveView.JS do

@doc "See `dispatch/2`."
def dispatch(%JS{} = js, event, opts) do
opts = validate_keys(opts, :dispatch, [:to, :detail, :bubbles])
opts = validate_keys(opts, :dispatch, [:to, :detail, :bubbles, :blocking])
args = [event: event, to: opts[:to]]

args =
Expand Down Expand Up @@ -284,6 +287,15 @@ defmodule Phoenix.LiveView.JS do
args
end

args =
case Keyword.get(opts, :blocking) do
true ->
Keyword.put(args, :blocking, opts[:blocking])

_ ->
args
end

put_op(js, "dispatch", args)
end

Expand Down
Loading