new formDialog signature

Massimo Melina committed Mar 22, 2023 at 19:00 UTC 36fc9e20ff06022205dec6d8e21b235c68fe1b6a
3 files changed +37 -31
admin/src/ArrayField.ts
+2 -2
@@ -33,7 +33,7 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
33 icon: Add,
34 title: "Add",
35 onClick: (event:any) =>
36 - formDialog({ fields }).then(o => // @ts-ignore
36 + formDialog({ form: { fields } }).then(o => // @ts-ignore
37 o && onChange([...value||[], o], { was: value, event }))
38 })
39 },
@@ -44,7 +44,7 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
44 icon: Edit,
45 title: "Modify",
46 onClick: (event:any) =>
47 - formDialog({ fields, values: row }).then(newRec => {
47 + formDialog<T>({ values: row, form: { fields } }).then(newRec => {
48 if (!newRec) return
49 const newValue = value!.map((oldRec, i) => i === $idx ? newRec : oldRec)
50 onChange(newValue, { was: value, event })
admin/src/OptionsPage.ts
+12 -8
@@ -287,14 +287,18 @@ export async function makeCertAndSave() {
287 return alertDialog("Retry this procedure on localhost", 'warning')
288 const res = await formDialog<{ commonName: string }>({
289 title: "We'll generate a basic certificate for you",
290 - fields: [
291 - h(Box, { display: 'flex', gap: 1, alignItems: 'center' }, h(Warning), "This certificate is just one click away, but will issue a warning on the browser"),
292 - { k: 'commonName', label: "Enter a domain, or leave empty" }
293 - ],
294 - save: { children: "Continue" },
295 - barSx: { gap: 1 },
296 - addToBar: [ h(Link, { target: 'cert', href: 'https://letsencrypt.org/' }, h(Button, {}, "Get better certificate")) ],
297 -
290 + form: {
291 + fields: [
292 + h(Box, { display: 'flex', gap: 1, alignItems: 'center' },
293 + h(Warning), "This certificate is just one click away, but will issue a warning on the browser"),
294 + { k: 'commonName', label: "Enter a domain, or leave empty" }
295 + ],
296 + save: { children: "Continue" },
297 + barSx: { gap: 1 },
298 + addToBar: [
299 + h(Link, { target: 'cert', href: 'https://letsencrypt.org/' }, h(Button, {}, "Get better certificate"))
300 + ],
301 + }
302 })
303 if (!res) return
304 const close = waitDialog()
admin/src/dialog.ts
+23 -21
@@ -6,14 +6,13 @@ import {
6 CircularProgress,
7 Dialog as MuiDialog,
8 DialogContent,
9 - DialogProps,
9 DialogTitle,
10 IconButton
11 } from '@mui/material'
12 import {
14 - createElement as h, Fragment,
13 + createElement as h, Dispatch, Fragment,
14 isValidElement,
16 - ReactElement,
15 + ReactElement, SetStateAction,
16 useEffect,
17 useRef,
18 useState
@@ -141,39 +140,43 @@ export async function confirmDialog(msg: string | ReactElement, { href }: Confir
140 }
141 }
142
144 -type FormDialog<T> = Pick<DialogProps, 'fullScreen' | 'title'>
145 - & Pick<DialogOptions, 'dialogProps'>
146 - & Omit<FormProps<T>, 'values' | 'save' | 'set'>
147 - & Partial<Pick<FormProps<T>, 'values' | 'save'>>
143 +type FormDialog<T> = Omit<FormProps<T>, 'values' | 'save' | 'set'>
144 + & Partial<Pick<FormProps<T>, 'save'>>
145 & {
149 - onChange?: (values:Partial<T>, extra: { setValues: React.Dispatch<React.SetStateAction<Partial<T>>> }) => void,
150 - before?: any
151 -}
152 -export async function formDialog<T>({ fullScreen, title, onChange, before, ...props }: FormDialog<T>) : Promise<T> {
146 + onChange?: (values:Partial<T>, extra: { setValues: Dispatch<SetStateAction<Partial<T>>> }) => void,
147 + before?: any
148 + }
149 +export async function formDialog<T>(
150 + { form, values, ...options }: Omit<DialogOptions, 'Content'> & {
151 + values?: Partial<T>,
152 + form: FormDialog<T> | ((values: Partial<T>) => FormDialog<T>),
153 + },
154 +) : Promise<T> {
155 return new Promise(resolve => newDialog({
156 className: 'dialog-confirm',
157 icon: '?',
158 onClose: resolve,
157 - title,
159 + ...options,
160 Content
161 }) )
162
163 function Content() {
162 - const [values, setValues] = useState<Partial<T>>(props.values||{})
164 + const [curValues, setCurValues] = useState<Partial<T>>(values||{})
165 + const { onChange, before, ...props } = typeof form === 'function' ? form(curValues) : form
166 return h(Fragment, {},
167 before,
168 h(Form, {
169 ...props,
167 - values,
170 + values: curValues,
171 set(v, k) {
169 - const newV = { ...values, [k]: v }
170 - setValues(newV)
171 - onChange?.(newV, { setValues })
172 + const newV = { ...curValues, [k]: v }
173 + setCurValues(newV)
174 + onChange?.(newV, { setValues: setCurValues })
175 },
176 save: {
177 ...props.save,
178 onClick() {
176 - closeDialog(values)
179 + closeDialog(curValues)
180 }
181 }
182 })
@@ -182,8 +185,7 @@ export async function formDialog<T>({ fullScreen, title, onChange, before, ...pr
185 }
186
187 export async function promptDialog(msg: string, props:any={}) : Promise<string | undefined> {
185 - return formDialog<{ text: string }>({
186 - ...props,
188 + return formDialog<{ text: string }>({ ...props, form: {
189 fields: [
190 { k: 'text', label: null, autoFocus: true,
191 before: h(Box, { mb: 2 }, msg),
@@ -201,7 +203,7 @@ export async function promptDialog(msg: string, props:any={}) : Promise<string |
203 h(Button, { onClick: closeDialog }, "Cancel"),
204 ...props.addToBar||[],
205 ]
204 - }).then(values => values?.text)
206 + } }).then(values => values?.text)
207 }
208
209 export function waitDialog() {