|
|
|
@ -4,28 +4,51 @@ export default class extends Controller { |
|
|
|
static targets = ["dialog"] |
|
|
|
|
|
|
|
confirm(event) { |
|
|
|
// The second pass — after the player confirmed — falls through to Turbo.
|
|
|
|
// The second pass -- after the player confirmed -- falls through to Turbo.
|
|
|
|
if (this.confirmed) return |
|
|
|
|
|
|
|
event.preventDefault() |
|
|
|
this.pendingForm = event.target |
|
|
|
this.dialogTarget.classList.remove("is-confirming", "is-leaving") |
|
|
|
this.leaving = false |
|
|
|
this.dialogTarget.showModal() |
|
|
|
} |
|
|
|
|
|
|
|
ok() { |
|
|
|
this.dialogTarget.close() |
|
|
|
// The dialog leaves upwards before the answer is sent, so the wait is however
|
|
|
|
// long that slide takes -- and nothing at all where the slide doesn't exist.
|
|
|
|
async ok() { |
|
|
|
if (this.leaving) return |
|
|
|
await this.#slideOut("is-confirming") |
|
|
|
|
|
|
|
// requestSubmit(), not submit(): the native call skips the submit event, so
|
|
|
|
// Turbo would miss it and navigate the whole page out of the game frame.
|
|
|
|
this.confirmed = true |
|
|
|
this.pendingForm?.requestSubmit() |
|
|
|
} |
|
|
|
|
|
|
|
cancel() { |
|
|
|
this.dialogTarget.close() |
|
|
|
async cancel() { |
|
|
|
if (this.leaving) return |
|
|
|
await this.#slideOut("is-leaving") |
|
|
|
this.pendingForm = null |
|
|
|
} |
|
|
|
|
|
|
|
backdropClick(event) { |
|
|
|
if (event.target === this.dialogTarget) this.cancel() |
|
|
|
} |
|
|
|
|
|
|
|
// The slide runs while the dialog is still open, so close() only ever lands
|
|
|
|
// once it is off screen: nothing is left behind for the browser to lay out,
|
|
|
|
// which is what Safari does with a dialog that outlives its top layer.
|
|
|
|
async #slideOut(state) { |
|
|
|
this.leaving = true |
|
|
|
this.dialogTarget.classList.add(state) |
|
|
|
|
|
|
|
// Reading a style forces the class change to resolve, so the transition
|
|
|
|
// exists by the time we ask for it.
|
|
|
|
getComputedStyle(this.dialogTarget).translate |
|
|
|
const running = this.dialogTarget.getAnimations({ subtree: false }) |
|
|
|
await Promise.all(running.map((animation) => animation.finished)).catch(() => {}) |
|
|
|
|
|
|
|
this.dialogTarget.close() |
|
|
|
} |
|
|
|
} |