@samitouri / QOSami-HFS / commits / 98b93474

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 }