share common code: dialogs
Massimo Melina committed
Mar 7, 2022 at 15:32 UTC
f8650d80ed99f9829eae7a3233ca87620a9428d9
3 files changed
+100
-138
admin/src/dialog.ts
+6
-56
@@ -3,46 +3,18 @@
3
import { Box, Button, Dialog as MuiDialog, DialogContent, DialogTitle } from '@mui/material'
4
import {
5
createElement as h,
6
- Fragment,
7
- FunctionComponent,
6
isValidElement,
7
ReactElement,
10
- ReactNode,
8
useEffect,
9
useRef
10
} from 'react'
14
-import { proxy, useSnapshot } from 'valtio'
11
import { Check, Error as ErrorIcon, Info, Warning } from '@mui/icons-material'
12
+import { newDialog, closeDialog, dialogsDefaults, DialogOptions, Dialogs } from 'shared/lib/dialogs'
13
+export { Dialogs }
14
+export * from 'shared/lib/dialogs'
15
17
-interface DialogOptions {
18
- Content: FunctionComponent,
19
- closable?: boolean,
20
- onClose?: (v?:any)=> any,
21
- className?: string,
22
- icon?: string | FunctionComponent,
23
- closableContent?: string | ReactNode,
24
- reserveClosing?: true
25
- noFrame?: boolean
26
- title?: string
27
- padding?: boolean
28
- dialogProps?: Record<string, any>,
29
-}
30
-
31
-const dialogs = proxy<DialogOptions[]>([])
32
-
33
-export const dialogsDefaults = {
34
- closableContent: 'x',
35
- padding: true,
36
-}
37
-
38
-export function Dialogs() {
39
- const snap = useSnapshot(dialogs)
40
- return h(Fragment, {},
41
- snap.length > 0 && snap.map((d,i) =>
42
- h(Dialog, { key:i, ...d })))
43
-}
44
-
45
-function Dialog(d:DialogOptions) {
16
+dialogsDefaults.Container = Container
17
+function Container(d:DialogOptions) {
18
useEffect(()=>{
19
ref.current?.focus()
20
}, [])
@@ -56,35 +28,13 @@ function Dialog(d:DialogOptions) {
28
},
29
d.title && h(DialogTitle, {}, d.title),
30
h(DialogContent, {
31
+ ref,
32
...d.dialogProps,
33
sx:{ ...d.dialogProps?.sx, px: p, pb: p }
34
}, h(d.Content) )
35
)
36
}
37
65
-export function newDialog(options: DialogOptions) {
66
- const $id = Math.random()
67
- ;(options as any).$id = $id // object identity is not working because of the proxy. This is a possible workaround
68
- dialogs.push(options)
69
- return (v?:any) => {
70
- const i = dialogs.findIndex(x => (x as any).$id === $id)
71
- if (i < 0) return
72
- dialogs.splice(i,1)
73
- options.onClose?.(v)
74
- }
75
-}
76
-
77
-export function closeDialog(v?:any) {
78
- let i = dialogs.length
79
- while (i--) {
80
- const d = dialogs[i]
81
- if (d.reserveClosing)
82
- continue
83
- dialogs.splice(i,1)
84
- return d.onClose?.(v)
85
- }
86
-}
87
-
38
type AlertType = 'error' | 'warning' | 'info' | 'success'
39
40
const type2ico = {
frontend/src/dialog.ts
+3
-82
@@ -1,88 +1,9 @@
1
// This file is part of HFS - Copyright 2020-2021, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
-import { createElement as h, Fragment, FunctionComponent, ReactElement, ReactNode, useEffect, useRef } from 'react'
4
-import { proxy, useSnapshot } from 'valtio'
3
+import { createElement as h, ReactElement, useEffect, useRef } from 'react'
4
import './dialog.css'
6
-
7
-interface DialogOptions {
8
- Content: FunctionComponent,
9
- closable?: boolean,
10
- onClose?: (v?:any)=> any,
11
- className?: string,
12
- icon?: string | FunctionComponent,
13
- closableContent?: string | ReactNode,
14
- reserveClosing?: true
15
- noFrame?: boolean
16
-}
17
-
18
-const dialogs = proxy<DialogOptions[]>([])
19
-
20
-export const dialogsDefaults = {
21
- closableContent: 'x',
22
-}
23
-
24
-export function Dialogs() {
25
- const snap = useSnapshot(dialogs)
26
- return h(Fragment, {},
27
- snap.length > 0 && snap.map((d,i) =>
28
- h(Dialog, { key:i, ...d })))
29
-}
30
-
31
-function Dialog(d:DialogOptions) {
32
- useEffect(()=>{
33
- ref.current?.focus()
34
- }, [])
35
- const ref = useRef<HTMLElement>()
36
- d = { ...dialogsDefaults, ...d }
37
- return h('div', {
38
- ref,
39
- className: 'dialog-backdrop '+(d.className||''),
40
- tabIndex: 0,
41
- onKeyDown,
42
- onClick: ()=> closeDialog()
43
- },
44
- d.noFrame ? h(d.Content || 'div')
45
- : h('div', {
46
- className: 'dialog',
47
- onClick(ev:any){
48
- ev.stopPropagation()
49
- }
50
- },
51
- d.closable || d.closable===undefined && h('button', { className:'dialog-icon dialog-closer', onClick:()=> closeDialog() }, d.closableContent),
52
- d.icon && h('div', { className:'dialog-icon dialog-type' }, d.icon),
53
- h('div', { className:'dialog-content' }, h(d.Content || 'div'))
54
- )
55
- )
56
-}
57
-
58
-function onKeyDown(ev:any) {
59
- if (ev.key === 'Escape') {
60
- closeDialog()
61
- }
62
-}
63
-
64
-export function newDialog(options: DialogOptions) {
65
- const $id = Math.random()
66
- ;(options as any).$id = $id // object identity is not working because of the proxy. This is a possible workaround
67
- dialogs.push(options)
68
- return (v?:any) => {
69
- const i = dialogs.findIndex(x => (x as any).$id === $id)
70
- if (i < 0) return
71
- dialogs.splice(i,1)
72
- options.onClose?.(v)
73
- }
74
-}
75
-
76
-export function closeDialog(v?:any) {
77
- let i = dialogs.length
78
- while (i--) {
79
- const d = dialogs[i]
80
- if (d.reserveClosing)
81
- continue
82
- dialogs.splice(i,1)
83
- return d.onClose?.(v)
84
- }
85
-}
5
+import { newDialog, closeDialog } from 'shared/lib/dialogs'
6
+export * from 'shared/lib/dialogs'
7
8
interface PromptOptions { def?:string, type?:string }
9
export async function promptDialog(msg: string, { def, type }:PromptOptions={}) : Promise<string | null> {
shared/src/dialogs.ts
new
+91
@@ -0,0 +1,91 @@
1
+import { createElement as h, Fragment, FunctionComponent, ReactNode, useEffect, useRef } from 'react'
2
+import { proxy, useSnapshot } from 'valtio'
3
+
4
+export interface DialogOptions {
5
+ Content: FunctionComponent,
6
+ closable?: boolean,
7
+ onClose?: (v?:any)=> any,
8
+ className?: string,
9
+ icon?: string | ReactNode,
10
+ closableContent?: string | ReactNode,
11
+ reserveClosing?: true
12
+ noFrame?: boolean
13
+ title?: string
14
+ padding?: boolean
15
+ dialogProps?: Record<string, any>,
16
+
17
+ Container?: FunctionComponent<DialogOptions>
18
+}
19
+
20
+const dialogs = proxy<DialogOptions[]>([])
21
+
22
+export const dialogsDefaults: Partial<DialogOptions> = {
23
+ closableContent: 'x',
24
+ padding: true,
25
+}
26
+
27
+export function Dialogs() {
28
+ const snap = useSnapshot(dialogs)
29
+ return h(Fragment, {},
30
+ snap.length > 0 && snap.map((d,i) =>
31
+ h(Dialog, { key:i, ...d })))
32
+}
33
+
34
+function Dialog(d:DialogOptions) {
35
+ const ref = useRef<HTMLElement>()
36
+ useEffect(()=>{
37
+ ref.current?.focus()
38
+ }, [])
39
+ d = { ...dialogsDefaults, ...d }
40
+ if (d.Container)
41
+ return h(d.Container, d)
42
+ return h('div', {
43
+ ref,
44
+ className: 'dialog-backdrop '+(d.className||''),
45
+ tabIndex: 0,
46
+ onKeyDown,
47
+ onClick: ()=> closeDialog()
48
+ },
49
+ d.noFrame ? h(d.Content || 'div')
50
+ : h('div', {
51
+ className: 'dialog',
52
+ onClick(ev:any){
53
+ ev.stopPropagation()
54
+ }
55
+ },
56
+ d.closable || d.closable===undefined && h('button', { className:'dialog-icon dialog-closer', onClick:()=> closeDialog() }, d.closableContent),
57
+ d.icon && h('div', { className:'dialog-icon dialog-type' }, d.icon),
58
+ h('div', { className:'dialog-content' }, h(d.Content || 'div'))
59
+ )
60
+ )
61
+}
62
+
63
+function onKeyDown(ev:any) {
64
+ if (ev.key === 'Escape') {
65
+ closeDialog()
66
+ }
67
+}
68
+
69
+export function newDialog(options: DialogOptions) {
70
+ const $id = Math.random()
71
+ ;(options as any).$id = $id // object identity is not working because of the proxy. This is a possible workaround
72
+ dialogs.push(options)
73
+ return (v?:any) => {
74
+ const i = dialogs.findIndex(x => (x as any).$id === $id)
75
+ if (i < 0) return
76
+ dialogs.splice(i,1)
77
+ options.onClose?.(v)
78
+ }
79
+}
80
+
81
+export function closeDialog(v?:any) {
82
+ let i = dialogs.length
83
+ while (i--) {
84
+ const d = dialogs[i]
85
+ if (d.reserveClosing)
86
+ continue
87
+ dialogs.splice(i,1)
88
+ return d.onClose?.(v)
89
+ }
90
+}
91
+