dialogs support 'back' to close
Massimo Melina committed
Mar 7, 2024 at 19:19 UTC
98b9347499056d8caf50fd6eb5f550652f09d157
1 file changed
+25
-2
shared/dialogs.ts
+25
-2
@@ -3,7 +3,7 @@
3
import { createElement as h, Fragment, FunctionComponent, isValidElement, ReactNode, useEffect, useRef,
4
HTMLAttributes, useState } from 'react'
5
import { proxy, ref, useSnapshot } from 'valtio'
6
-import { isPrimitive, objSameKeys } from '.'
6
+import { domOn, isPrimitive, objSameKeys } from '.'
7
8
export interface DialogOptions {
9
Content: FunctionComponent<any>,
@@ -19,6 +19,7 @@ export interface DialogOptions {
19
position?: [number, number]
20
dialogProps?: Record<string, any>
21
$id?: number
22
+ ts?: number
23
24
Container?: FunctionComponent<DialogOptions>
25
}
@@ -56,7 +57,20 @@ function isDescendant(child: Node | null, parent: Node) {
57
return false
58
}
59
60
+let ignorePopState = false
61
+function back() {
62
+ ignorePopState = true
63
+ window.history.back()
64
+}
65
+
66
export function Dialogs(props: HTMLAttributes<HTMLDivElement>) {
67
+ useEffect(() => domOn('popstate', ev => {
68
+ if (ignorePopState)
69
+ return ignorePopState = false
70
+ if (ev.state.ts > dialogs[dialogs.length - 1]?.ts!) // forward
71
+ return back() // cancel
72
+ closeDialog(undefined, true)
73
+ }), [])
74
const snap = useSnapshot(dialogs)
75
useEffect(() => {
76
document.body.style.overflow = snap.length ? 'hidden' : ''
@@ -142,9 +156,13 @@ function onKeyDown(ev:any) {
156
157
export function newDialog(options: DialogOptions) {
158
const $id = Math.random()
159
+ const ts = performance.now()
160
options.$id = $id // object identity is not working because of the proxy. This is a possible workaround
161
+ options.ts = ts
162
options = objSameKeys(options, x => isValidElement(x) ? ref(x) : x) as typeof options // encapsulate elements as react will try to write, but valtio makes them readonly
163
dialogs.push(options)
164
+ if (options.closable !== false)
165
+ window.history.pushState({ $id, ts }, '')
166
return { close }
167
168
function close(v?:any) {
@@ -154,12 +172,17 @@ export function newDialog(options: DialogOptions) {
172
}
173
}
174
157
-export function closeDialog(v?:any) {
175
+export function closeDialog(v?:any, skipHistory=false) {
176
let i = dialogs.length
177
+ if (dialogs[i - 1]?.closable === false) return
178
while (i--) {
179
const d = dialogs[i]
180
if (d.reserveClosing)
181
continue
182
+ if (!skipHistory) {
183
+ if (window.history.state.$id !== d.$id) return
184
+ back()
185
+ }
186
closeDialogAt(i, v)
187
return d
188
}