better code
Massimo Melina committed
Dec 5, 2024 at 20:34 UTC
5039f1c055d6276777f6e9d9d6e1ad6d7383dd5c
1 file changed
+18
-16
shared/dialogs.ts
+18
-16
@@ -20,15 +20,19 @@ export interface DialogOptions {
20
padding?: boolean
21
position?: [number, number]
22
dialogProps?: Record<string, any>
23
+ restoreFocus?: boolean
24
+ Container?: FunctionComponent<DialogOptions>
25
+}
26
+
27
+interface Dialog extends DialogOptions {
28
$id?: number
29
$opening?: NodeJS.Timeout
30
ts?: number
31
+ close: (v?: any) => void
32
restoreFocus?: any
27
-
28
- Container?: FunctionComponent<DialogOptions>
33
}
34
31
-const dialogs = proxy<DialogOptions[]>([])
35
+const dialogs = proxy<Dialog[]>([])
36
const { history } = window
37
38
export const dialogsDefaults: Partial<DialogOptions> = {
@@ -181,27 +185,24 @@ export function componentOrNode(x: ReactNode | FunctionComponent) {
185
export function newDialog(options: DialogOptions) {
186
const $id = Math.random()
187
const ts = performance.now()
184
- options.$id = $id // object identity is not working because of the proxy. This is a possible workaround
185
- options.ts = ts
186
- if (document.activeElement)
187
- options.restoreFocus ??= ref(document.activeElement)
188
- 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
188
+ const d: Dialog = Object.assign(objSameKeys(options, x => isValidElement(x) ? ref(x) : x) as typeof options, { // encapsulate elements as React will try to write, but valtio makes them readonly
189
+ close, ts, $id, // object identity is not working because it's proxied (valtio). This is a possible workaroundu
190
+ restoreFocus: options.restoreFocus ?? ref(document.activeElement || {}),
191
+ })
192
let cancelOpening = false
193
waitClosing.then(() => { // in case dialogs were just closed, account for window.history delay. You already didn't expect dialog to open immediately, but at state change
194
if (cancelOpening) return
192
- dialogs.push(options)
193
- options = dialogs[dialogs.length - 1] // replace with proxy object, to stay in sync with its changes
194
- if (options.closable !== false)
195
+ dialogs.push(d)
196
+ if (dialogs.at(-1)!.closable !== false) // use proxy object, to stay in sync with its changes
197
history.pushState({ $dialog: $id, ts, idx: 1 + (history.state?.idx || 0) }, '')
198
})
197
- return { close }
199
+ return d
200
201
function close(v?:any) {
200
- cancelOpening = true // in case it was not open yet
202
+ cancelOpening = true
203
const i = dialogs.findIndex(x => (x as any).$id === $id)
204
if (i < 0) return
203
- if (history.state?.$dialog === $id)
204
- options.closed = back()
205
+ d.closed = history.state?.$dialog === $id ? back() : Promise.resolve()
206
closeDialogAt(i, v)
207
return options.closed
208
}
@@ -227,7 +228,8 @@ function closeDialogAt(i: number, value?: any) {
228
const [d] = dialogs.splice(i,1)
229
d.restoreFocus?.focus?.() // if element is not HTMLElement, it doesn't have focus method
230
d.closingValue = value && typeof value === 'object' ? ref(value) : value // since this is being assigned to a valtio proxy, ref is necessary to avoid crashing with unusual (and possibly accidental) objects like React's SynteticEvents
230
- Promise.resolve(d.closed).then(() =>
231
+ d.closed ??= Promise.resolve()
232
+ d.closed.then(() =>
233
d?.onClose?.(value))
234
return d
235
}