main
ts 78 lines 2.98 KB
Raw
1 import { dirname, enforceFinal, join } from './misc'
2 import _ from 'lodash'
3 import { useApiList } from './api'
4 import { ChangeEvent, createElement as h, useMemo } from 'react'
5 import { Autocomplete, AutocompleteProps, TextField } from '@mui/material'
6 import { FieldProps } from '@hfs/mui-grid-form'
7
8 interface VfsPathFieldProps extends FieldProps<string> {
9 autocompleteProps: Partial<AutocompleteProps<string, false, true, undefined>>
10 }
11
12 export default function VfsPathField({
13 value='', onChange, helperText, setApi, autocompleteProps, folders=true, files=true,
14 InputLabelProps, slotProps, ...props
15 }: VfsPathFieldProps) {
16 const uri = dirname(value.replace(/\/{2,}/g, '/'))
17 const { list, loading } = useApiList('get_file_list', {
18 uri,
19 admin: true,
20 fileMask: typeof files === 'string' ? files : undefined,
21 onlyFolders: !files
22 })
23 const options = useMemo(() => {
24 const ret = _.uniq([uri && (dirname(uri) + '/'), enforceFinal('/', uri)].filter(Boolean))
25 .concat(list.map(x => join(uri, x.n)))
26 if (value && !ret.includes(value)) ret.push(value) // allow re-selection of the same value without issuing a console warning
27 return ret
28 }, [list, uri])
29 setApi?.({
30 getError() {
31 return !folders && value?.endsWith('/') && "must be a file" || false
32 }
33 })
34 return h(Autocomplete<string, false, true, undefined>, {
35 value,
36 options,
37 isOptionEqualToValue: (o,v) => o === v || o === v + '/',
38 loading,
39 disableClearable: true,
40 disableCloseOnSelect: true,
41 renderInput: params => h(TextField, {
42 helperText,
43 onBlur(event) {
44 // if the user specified a folder without the final slash, try to enforce it
45 const v = enforceFinal('/', event.target.value)
46 if (options.includes(v))
47 onChange(v, { was: value, event })
48 },
49 ...params,
50 ...props,
51 slotProps: {
52 ...slotProps,
53 input: {
54 ...slotProps?.input,
55 ...params.slotProps.input,
56 },
57 inputLabel: {
58 shrink: true,
59 ...params.slotProps.inputLabel,
60 ...InputLabelProps,
61 ...slotProps?.inputLabel,
62 },
63 htmlInput: {
64 ...slotProps?.htmlInput,
65 ...params.slotProps.htmlInput,
66 onChange(event: ChangeEvent<HTMLInputElement>) {
67 params.slotProps.htmlInput.onChange?.(event)
68 const v = event.target.value
69 if (files || !v || v.endsWith('/'))
70 onChange(v, { was: value, event })
71 },
72 },
73 },
74 }),
75 onChange: (event, sel) => onChange(sel, { was: value, event }),
76 ...autocompleteProps,
77 })
78 }