@samitouri / QOSami-HFS / commits / 5b1f28b1

dx: ArrayField.saveOn and formDialog.save:false

Massimo Melina committed Jun 30, 2025 at 23:48 UTC 5b1f28b11c01482dd38e0af925fd40be5bb33532
3 files changed +37 -15
admin/src/ArrayField.ts
+32 -12
@@ -27,18 +27,23 @@ type ArrayFieldProps<T> = FieldProps<T[] | Dict<T>> & {
27 dialog?: Partial<DialogOptions>,
28 details?: boolean
29 objectK?: string
30 + saveOn?: 'change' | 'close'
31 }
31 -export function ArrayField<T extends object>({ label, helperText, fields, value, onChange, onError, setApi, reorder, prepend, noRows, valuesForAdd, autoRowHeight, dialog, form, details, objectK, ...rest }: ArrayFieldProps<T>) {
32 +export function ArrayField<T extends object>({
33 + label, helperText, fields, value, onChange, onError, setApi, reorder, prepend, noRows, valuesForAdd, autoRowHeight,
34 + dialog, form, details, objectK, saveOn, ...rest
35 +}: ArrayFieldProps<T>) {
36 const valueA = Array.isArray(value) ? value
37 : !objectK || !value ? [] // avoid crash if non-array values are passed, especially developing plugins
38 : Object.entries(value).map(([k, v]) => ({ [objectK]: k, ...v }))
39 const rows = useMemo(() => valueA!.map((x,$idx) =>
40 setHidden({ ...x } as any, x.hasOwnProperty('id') ? { $idx } : { id: $idx })),
41 [JSON.stringify(valueA)]) //eslint-disable-line
38 - const formProp = (values: any) => ({
42 + const getFormProp = (more: any) => (values: any) => ({
43 fields: callable(fields, values).map(({ $width, $column, $type, $hideUnder, showIf, $render, $mergeRender, ...rest }) =>
44 (!showIf || showIf(values)) && _.defaults(rest, byType[$type]?.field)),
45 ...form,
46 + ...more,
47 })
48 setApi?.({ isEqual: isOrderedEqual }) // don't rely on stringify, as it wouldn't work with non-json values
49 const [undo, setUndo] = useState<typeof valueA>()
@@ -98,14 +103,19 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
103 icon: Add,
104 title,
105 size: 'small',
101 - onClick: ev =>
102 - formDialog<T>({ form: formProp, title, values: valuesForAdd, dialogProps: dialog }).then(x => {
103 - if (!x) return
104 - const newValue = valueA.slice() || []
105 - if (prepend) newValue.unshift(x)
106 - else newValue.push(x)
107 - set(newValue, ev)
106 + async onClick(ev) {
107 + const res = await formDialog<T>({
108 + form: getFormProp({}),
109 + title,
110 + values: valuesForAdd,
111 + dialogProps: dialog
112 })
113 + if (!res) return
114 + const newValue = valueA.slice() || []
115 + if (prepend) newValue.unshift(res)
116 + else newValue.push(res)
117 + set(newValue, ev)
118 + }
119 }),
120 undo !== undefined && h(IconBtn, {
121 icon: Undo,
@@ -127,12 +137,17 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
137 ev.stopPropagation()
138 const res = await formDialog<T>({
139 values: row as any,
130 - form: formProp,
140 + form: getFormProp(saveOn === 'change' && {
141 + save: false,
142 + onChange(values: T) {
143 + updateRec($idx, values, 'change')
144 + }
145 + } || saveOn && { save: false }),
146 title: h(Fragment, {}, title, label && ' - ', label),
147 dialogProps: dialog
148 })
149 if (res)
135 - set(valueA.map((oldRec, i) => i === $idx ? res : oldRec), ev)
150 + updateRec($idx, res, ev)
151 }
152 }),
153 h(GridActionsCellItem as any, {
@@ -170,10 +185,15 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
185 )
186 )
187
188 + function updateRec($idx: number, rec: T, ev?: any) {
189 + set(valueA.map((oldRec, i) => i === $idx ? rec : oldRec), ev)
190 + }
191 +
192 function set(newValue: typeof valueA, event?: any) {
193 onChange(!objectK ? newValue : Object.fromEntries((newValue as any).map(({ [objectK]: k, ...v }) => [k, v])),
194 { was: value, event })
176 - setUndo(valueA)
195 + if (saveOn !== 'change')
196 + setUndo(valueA)
197 }
198
199 }
admin/src/dialog.ts
+5 -2
@@ -153,15 +153,18 @@ export async function formDialog<T>(
153 Wrapper?: FC
154 },
155 ) : Promise<T> {
156 + let exposedValues: typeof values
157 return new Promise(resolve => {
158 const dialog = newDialog({
159 className: 'dialog-form',
159 - onClose: resolve,
160 + onClose: x => resolve(x || exposedValues),
161 ...options,
162 Content() {
163 const [curValues, setCurValues] = useState<Partial<T>>(values||{})
164 const { onChange, before, ...props } = typeof form === 'function' ? form(curValues) : form
164 - return h(Wrapper || Fragment, {},
165 + if (props.save === false)
166 + exposedValues = curValues
167 + return h(Fragment, {},
168 before,
169 h(Form, {
170 ...props,
mui-grid-form/StringField.ts
-1
@@ -78,7 +78,6 @@ export function StringField({ value, onChange, min, max, required, setApi, typin
78 })
79 return !suggestions ? render(null)
80 : h(Autocomplete, {
81 - value,
81 freeSolo: true,
82 options: suggestions,
83 renderInput: render,