Mattias Bodlund 3 weeks ago
parent
commit
d0dee46287
2 changed files with 71 additions and 5 deletions
  1. +43
    -0
      app/assets/stylesheets/application.css
  2. +28
    -5
      app/javascript/chance_controller.js

+ 43
- 0
app/assets/stylesheets/application.css View File

@ -1080,6 +1080,49 @@ dialog::backdrop {
background: rgba(0,0,0,.5);
}
@media (prefers-reduced-motion: no-preference) {
dialog:not(.facts-modal)[open] {
translate: 0 0;
transition: translate var(--dur-base) var(--ease-out);
}
@starting-style {
dialog:not(.facts-modal)[open] {
translate: 0 100dvh;
}
}
dialog:not(.facts-modal)[open].is-leaving,
dialog:not(.facts-modal)[open].is-confirming {
pointer-events: none;
transition-timing-function: var(--ease-in);
}
dialog:not(.facts-modal)[open].is-leaving {
translate: 0 100dvh;
}
dialog:not(.facts-modal)[open].is-confirming {
translate: 0 -100dvh;
}
dialog:not(.facts-modal)[open]::backdrop {
opacity: 1;
transition: opacity var(--dur-base) var(--ease-out);
}
@starting-style {
dialog:not(.facts-modal)[open]::backdrop {
opacity: 0;
}
}
dialog:not(.facts-modal)[open].is-leaving::backdrop,
dialog:not(.facts-modal)[open].is-confirming::backdrop {
opacity: 0;
}
}
.header-label {
text-transform: uppercase;
font: var(--td-s);


+ 28
- 5
app/javascript/chance_controller.js View File

@ -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()
}
}

Loading…
Cancel
Save