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,