-
Notifications
You must be signed in to change notification settings - Fork 589
/
Copy pathdialog.ts
97 lines (83 loc) · 2.69 KB
/
dialog.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// TODO: move to misc.ts
import {useEffect, useState} from 'react'
import {type To} from 'react-router-dom'
import {useQueryParams} from '@/hooks/use-query-params'
import {SettingsDialogKey} from '@/routes/settings'
import {sleep} from '@/utils/misc'
export const EXIT_DURATION_MS = 200
export type GlobalDialogKey = 'logout' | 'live-usage'
export type AppStoreDialogKey = 'updates' | 'add-community-store' | 'default-credentials' | 'app-settings'
export type FilesDialogKey =
| 'files-share-info'
| 'files-empty-trash-confirmation'
| 'files-extension-change-confirmation'
| 'files-permanently-delete-confirmation'
| 'files-external-storage-unsupported'
export type DialogKey = GlobalDialogKey | AppStoreDialogKey | SettingsDialogKey | FilesDialogKey
// TODO: make dialog query params typesafe
/**
* For use with dialogs and other Radix elements with an `onOpenChange` prop.
*/
export function afterDelayedClose(cb?: () => void) {
return (open: boolean) => !open && sleep(EXIT_DURATION_MS).then(cb)
}
export function useAfterDelayedClose(open: boolean, cb: () => void) {
useEffect(() => {
const id = setTimeout(() => {
if (!open) cb()
}, EXIT_DURATION_MS)
// Cancel the timeout if the component unmounts or the `open` prop changes.
return () => clearTimeout(id)
}, [open, cb])
}
/** Allow controlling dialog from query params */
export function useDialogOpenProps(dialogKey: DialogKey) {
const {params, add, filter} = useQueryParams()
const [open, setOpen] = useState(false)
// Update open state when url is changed from the outside
useEffect(() => {
setOpen(params.get('dialog') === dialogKey)
}, [params, dialogKey])
const addQueryParam = () => {
add('dialog', dialogKey)
}
const removeQueryParam = async () => {
await sleep(EXIT_DURATION_MS)
// Remove `dialog` and all `dialogKey` prefixed search params
filter(([key]) => {
const isDialog = key === 'dialog'
const dialogParams = key.startsWith(dialogKey)
return !(isDialog || dialogParams)
})
}
const onOpenChange = (open: boolean) => {
// Keeping this here despite `useEffect` to change open state immediately
setOpen(open)
if (open) {
addQueryParam()
} else {
removeQueryParam()
}
}
return {open, onOpenChange}
}
/** For react router */
export function useLinkToDialog() {
const {addLinkSearchParams} = useQueryParams()
return (
dialogKey: DialogKey,
otherParams?: {
[key: string]: string
},
): To => {
const otherParamsModified: {[key: string]: string} = {}
if (otherParams) {
Object.keys(otherParams).forEach((key) => {
otherParamsModified[`${dialogKey}-${key}`] = otherParams[key]
})
}
return {
search: addLinkSearchParams({dialog: dialogKey, ...otherParamsModified}),
}
}
}