@samitouri / QOSami-HFS / commits / a2e147d8

admin/options: support long wrapping block-ip rules

Massimo Melina committed Jun 23, 2024 at 00:07 UTC a2e147d8aaa9482121dd059ddb0e1da41b277431
4 files changed +15 -10
admin/src/ArrayField.ts
+8 -5
@@ -11,8 +11,8 @@ import { DateTimeField } from './DateTimeField'
11 import _ from 'lodash'
12 import { Center, IconBtn } from './mui'
13
14 -type ArrayFieldProps<T> = FieldProps<T[]> & { fields: FieldDescriptor[], height?: number, reorder?: boolean, prepend?: boolean }
15 -export function ArrayField<T extends object>({ label, helperText, fields, value, onChange, onError, setApi, reorder, prepend, noRows, valuesForAdd, ...rest }: ArrayFieldProps<T>) {
14 +type ArrayFieldProps<T> = FieldProps<T[]> & { fields: FieldDescriptor[], height?: number, reorder?: boolean, prepend?: boolean, autoRowHeight?: boolean }
15 +export function ArrayField<T extends object>({ label, helperText, fields, value, onChange, onError, setApi, reorder, prepend, noRows, valuesForAdd, autoRowHeight, ...rest }: ArrayFieldProps<T>) {
16 const rows = useMemo(() => (value||[]).map((x,$idx) =>
17 setHidden({ ...x } as any, x.hasOwnProperty('id') ? { $idx } : { id: $idx })),
18 [JSON.stringify(value)]) //eslint-disable-line
@@ -27,7 +27,10 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
27 h(Box, { ...rest },
28 h(DataGrid, {
29 rows,
30 - sx: { '.MuiDataGrid-virtualScroller': { minHeight: '3em' } },
30 + ...autoRowHeight && { getRowHeight: () => 'auto' as const },
31 + sx: { '.MuiDataGrid-virtualScroller': { minHeight: '3em' },
32 + ...autoRowHeight && { '.MuiDataGrid-cell': { minHeight: '52px !important' } }
33 + },
34 hideFooterSelectedRowCount: true,
35 hideFooter: true,
36 slots: {
@@ -42,7 +45,7 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
45 columns: [
46 ...fields.map(f => {
47 const def = byType[f.$type]?.column
45 - return ({
48 + return {
49 field: f.k,
50 headerName: f.headerName ?? (typeof f.label === 'string' ? f.label : labelFromKey(f.k)),
51 disableColumnMenu: true,
@@ -52,7 +55,7 @@ export function ArrayField<T extends object>({ label, helperText, fields, value,
55 ...def,
56 ...f.$width ? { [f.$width >= 8 ? 'width' : 'flex']: f.$width } : (!def?.width && !def?.flex && { flex: 1 }),
57 ...f.$column,
55 - })
58 + }
59 }),
60 {
61 field: '',
admin/src/FileField.ts
+1 -2
@@ -14,8 +14,7 @@ export default function FileField({ value, onChange, files=true, folders=false,
14 ...props,
15 value,
16 onChange,
17 - onTyping: (v: string) => !v.includes('\n') && v,
18 - InputProps: { multiline: true },
17 + wrap: true,
18 end: h(IconBtn, {
19 icon: Eject,
20 title: "Browse files...",
admin/src/OptionsPage.ts
+2 -2
@@ -139,9 +139,9 @@ export default function OptionsPage() {
139 },
140 { k: 'allowed_referer', placeholder: "any", label: "Links from other websites", comp: AllowedReferer, },
141
142 - { k: 'block', label: false, comp: ArrayField, prepend: true, sm: true,
142 + { k: 'block', label: false, comp: ArrayField, prepend: true, sm: true, autoRowHeight: true,
143 fields: [
144 - { k: 'ip', label: "Blocked IP", sm: 6, required: true, helperText: h(WildcardsSupported) },
144 + { k: 'ip', label: "Blocked IP", sm: 12, required: true, wrap: true, helperText: h(WildcardsSupported) },
145 { k: 'expire', $type: 'dateTime', minDate: new Date(), sm: 6, helperText: "Leave empty for no expiration" },
146 {
147 k: 'disabled',
mui-grid-form/StringField.ts
+4 -1
@@ -13,8 +13,9 @@ interface StringFieldProps extends FieldProps<string>, Partial<Omit<StandardText
13 required?: boolean
14 start?: ReactNode
15 end?: ReactNode
16 + wrap?: boolean
17 }
17 -export function StringField({ value, onChange, min, max, required, setApi, typing, start, end, onTyping, suggestions, ...props }: StringFieldProps) {
18 +export function StringField({ value, onChange, min, max, required, setApi, typing, start, end, onTyping, suggestions, wrap, ...props }: StringFieldProps) {
19 const normalized = value ?? ''
20 setApi?.({
21 getError() {
@@ -42,6 +43,7 @@ export function StringField({ value, onChange, min, max, required, setApi, typin
43 value: state,
44 onChange(ev) {
45 let val = ev.target.value
46 + if (wrap && val.includes('\n')) return // prevent newlines, we are a wrapped yet single line
47 if (onTyping) {
48 const res = onTyping(val)
49 if (res === false) return
@@ -66,6 +68,7 @@ export function StringField({ value, onChange, min, max, required, setApi, typin
68 go(ev)
69 },
70 InputProps: {
71 + ...wrap && { multiline: true },
72 startAdornment: start && h(InputAdornment, { position: 'start' }, start),
73 endAdornment: end && h(InputAdornment, { position: 'end' }, end),
74 ...props.InputProps,