@samitouri / QOSami-HFS / commits / 49c06344

better code: simplified dom

Massimo Melina committed Apr 22, 2024 at 16:43 UTC 49c0634495bba1724023dd069f391bda84172671
2 files changed +88 -102
admin/src/AccountForm.ts
+1 -1
@@ -31,7 +31,7 @@ export default function AccountForm({ account, done, groups, addToBar, reload }:
31 const ref = useRef<HTMLFormElement>()
32 const expired = Boolean(values.expire)
33 return h(Form, {
34 - formRef: ref,
34 + formRef: ref,
35 values,
36 set(v, k) {
37 setValues(values => ({ ...values, [k]: v }))
mui-grid-form/index.ts
+87 -101
@@ -1,16 +1,7 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import {
4 - createElement as h,
5 - FC,
6 - Fragment,
7 - HTMLProps,
8 - isValidElement,
9 - ReactElement,
10 - ReactNode,
11 - useEffect,
12 - useState,
13 - useRef,
4 + createElement as h, FC, Fragment, isValidElement, ReactElement, ReactNode, useEffect, useState, useRef,
5 MutableRefObject
6 } from 'react'
7 import { Box, BoxProps, Button, Grid } from '@mui/material'
@@ -73,7 +64,6 @@ export interface FormProps<Values> extends Partial<BoxProps> {
64 formRef?: MutableRefObject<HTMLFormElement | undefined>
65 saveOnEnter?: boolean
66 gridProps?: Partial<GridProps>
76 - formProps?: HTMLProps<HTMLFormElement>
67 }
68 enum Phase { Idle, WaitValues, Validating }
69
@@ -90,7 +80,6 @@ export function Form<Values extends Dict>({
80 onError,
81 saveOnEnter,
82 gridProps,
93 - formProps,
83 ...rest
84 }: FormProps<Values>) {
85 const mounted = useRef(false)
@@ -111,104 +100,101 @@ export function Form<Values extends Dict>({
100 const keyMet: Dict<number> = {}
101
102 const apis: Dict<FieldApi<unknown>> = {} // consider { [K in keyof Values]?: FieldApi<Values[K]> }
114 - return h('form', {
115 - ref: formRef && (x => formRef.current = x ? x as HTMLFormElement : undefined),
116 - ...formProps,
103 + return h(Box, {
104 + component: 'form',
105 + display: 'flex',
106 + flexDirection: 'column',
107 + gap: 3,
108 + ref: formRef,
109 onSubmit(ev) {
110 ev.preventDefault()
111 },
112 onKeyDown(ev) {
113 if (saveBtn && !saveBtn.disabled && (ev.ctrlKey || ev.metaKey) && ev.key === 'Enter')
114 pleaseSubmit()
123 - }
115 + },
116 + ...rest,
117 },
125 - h(Box, {
118 + h(Grid, { container:true, rowSpacing:3, columnSpacing:1, ...gridProps },
119 + fields.map((row, idx) => {
120 + if (!row)
121 + return null
122 + if (isValidElement(row))
123 + return h(Grid, { key: idx, item: true, xs: 12 }, row)
124 + const { k, fromField=_.identity, toField=_.identity, getError, error, ...field } = row
125 + let errMsg = errors[k] || error || fieldExceptions[k]
126 + if (errMsg === true)
127 + errMsg = "Not valid"
128 + if (k) {
129 + const originalValue = row.hasOwnProperty('value') ? row.value : values?.[k]
130 + const whole = { ...row, ...field }
131 + Object.assign(field, {
132 + value: toField(originalValue),
133 + error: Boolean(errMsg || error) || undefined,
134 + setApi(api) { apis[k] = api },
135 + onKeyDown(event: any) {
136 + if (saveOnEnter && event.key === 'Enter')
137 + pleaseSubmit()
138 + },
139 + onChange(v: unknown) {
140 + try {
141 + v = fromField(v, { originalValue })
142 + setFieldExceptions(x => ({ ...x, [k]: false }))
143 + if ((apis[k]?.isEqual || _.isEqual)(v, originalValue)) return
144 + set(v, k)
145 + pleaseValidate(k)
146 + }
147 + catch (e) {
148 + setFieldExceptions(x => ({ ...x, [k]: (e as any)?.message || String(e) || true }))
149 + }
150 + },
151 + } as Partial<FieldProps<any>>)
152 + if (Array.isArray(field.helperText))
153 + field.helperText = h(Fragment, {}, ...field.helperText)
154 + if (errMsg) // special rendering when we have both error and helperText. "hr" would be nice but issues a warning because contained in a <p>
155 + field.helperText = !field.helperText ? errMsg
156 + : h(Fragment, {},
157 + h('span', { style: { borderBottom: '1px solid' } }, errMsg),
158 + h(Box, { color: 'text.primary', component: 'span', /*avoid console warning*/ display: 'block' },
159 + field.helperText),
160 + )
161 + if (field.label === undefined)
162 + field.label = labelFromKey(k)
163 + _.defaults(field, defaults?.(whole))
164 + }
165 + {
166 + const { xs=12, sm, md, lg, xl, comp=StringField, before, after,
167 + fromField, toField, // don't propagate
168 + ...rest } = field
169 + Object.assign(rest, { name: k })
170 + const n = (keyMet[k] = (keyMet[k] || 0) + 1)
171 + return h(Grid, { key: k ? k + n : idx, item: true, xs, sm, md, lg, xl },
172 + before,
173 + isValidElement(comp) ? comp : h(comp, rest),
174 + after
175 + )
176 + }
177 + })
178 + ),
179 + saveBtn && h(Box, {
180 display: 'flex',
127 - flexDirection: 'column',
128 - gap: 3,
129 - ...rest
181 + alignItems: 'center',
182 + sx: Object.assign({},
183 + stickyBar && {
184 + width: 'fit-content', zIndex: 2, backgroundColor: 'background.paper', borderRadius: 1,
185 + position: 'sticky', bottom: 0, p: 1, m: -1, boxShadow: '0px 0px 30px #000',
186 + },
187 + barSx)
188 },
131 - h(Grid, { container:true, rowSpacing:3, columnSpacing:1, ...gridProps },
132 - fields.map((row, idx) => {
133 - if (!row)
134 - return null
135 - if (isValidElement(row))
136 - return h(Grid, { key: idx, item: true, xs: 12 }, row)
137 - const { k, fromField=_.identity, toField=_.identity, getError, error, ...field } = row
138 - let errMsg = errors[k] || error || fieldExceptions[k]
139 - if (errMsg === true)
140 - errMsg = "Not valid"
141 - if (k) {
142 - const originalValue = row.hasOwnProperty('value') ? row.value : values?.[k]
143 - const whole = { ...row, ...field }
144 - Object.assign(field, {
145 - value: toField(originalValue),
146 - error: Boolean(errMsg || error) || undefined,
147 - setApi(api) { apis[k] = api },
148 - onKeyDown(event: any) {
149 - if (saveOnEnter && event.key === 'Enter')
150 - pleaseSubmit()
151 - },
152 - onChange(v: unknown) {
153 - try {
154 - v = fromField(v, { originalValue })
155 - setFieldExceptions(x => ({ ...x, [k]: false }))
156 - if ((apis[k]?.isEqual || _.isEqual)(v, originalValue)) return
157 - set(v, k)
158 - pleaseValidate(k)
159 - }
160 - catch (e) {
161 - setFieldExceptions(x => ({ ...x, [k]: (e as any)?.message || String(e) || true }))
162 - }
163 - },
164 - } as Partial<FieldProps<any>>)
165 - if (Array.isArray(field.helperText))
166 - field.helperText = h(Fragment, {}, ...field.helperText)
167 - if (errMsg) // special rendering when we have both error and helperText. "hr" would be nice but issues a warning because contained in a <p>
168 - field.helperText = !field.helperText ? errMsg
169 - : h(Fragment, {},
170 - h('span', { style: { borderBottom: '1px solid' } }, errMsg),
171 - h(Box, { color: 'text.primary', component: 'span', /*avoid console warning*/ display: 'block' },
172 - field.helperText),
173 - )
174 - if (field.label === undefined)
175 - field.label = labelFromKey(k)
176 - _.defaults(field, defaults?.(whole))
177 - }
178 - {
179 - const { xs=12, sm, md, lg, xl, comp=StringField, before, after,
180 - fromField, toField, // don't propagate
181 - ...rest } = field
182 - Object.assign(rest, { name: k })
183 - const n = (keyMet[k] = (keyMet[k] || 0) + 1)
184 - return h(Grid, { key: k ? k + n : idx, item: true, xs, sm, md, lg, xl },
185 - before,
186 - isValidElement(comp) ? comp : h(comp, rest),
187 - after
188 - )
189 - }
190 - })
191 - ),
192 - saveBtn && h(Box, {
193 - display: 'flex',
194 - alignItems: 'center',
195 - sx: Object.assign({},
196 - stickyBar && {
197 - width: 'fit-content', zIndex: 2, backgroundColor: 'background.paper', borderRadius: 1,
198 - position: 'sticky', bottom: 0, p: 1, m: -1, boxShadow: '0px 0px 30px #000',
199 - },
200 - barSx)
201 - },
202 - h(LoadingButton, {
203 - variant: 'contained',
204 - startIcon: h(Save),
205 - children: "Save",
206 - loading: useDebounce(phase !== Phase.Idle), // debounce fixes click being ignored at state change, and flickering
207 - ...saveBtn,
208 - onClick: pleaseSubmit,
209 - }),
210 - ...addToBar,
211 - )
189 + h(LoadingButton, {
190 + variant: 'contained',
191 + startIcon: h(Save),
192 + children: "Save",
193 + loading: useDebounce(phase !== Phase.Idle), // debounce fixes click being ignored at state change, and flickering
194 + ...saveBtn,
195 + onClick: pleaseSubmit,
196 + }),
197 + ...addToBar,
198 )
199 )
200