admin: nicer file-picker
Massimo Melina committed
Jan 30, 2024 at 15:11 UTC
d774776aed697bd6a8963907c1d7cf49e5849e4f
3 files changed
+67
-65
admin/src/FileField.ts
+32
-34
@@ -16,41 +16,39 @@ export default function FileField({ value, onChange, files=true, folders=false,
16
value,
17
onChange,
18
onTyping: (v: string) => !v.includes('\n') && v,
19
- InputProps: {
20
- multiline: true,
21
- endAdornment: h(InputAdornment, { position: 'end' },
22
- h(IconBtn, {
23
- icon: Eject,
24
- title: "Browse files...",
25
- edge: 'end',
26
- onClick() {
27
- const { close } = newDialog({
28
- title: title ?? (files ? "Pick a file" : "Pick a folder"),
29
- dialogProps: {
30
- fullScreen: !large,
31
- sx: { minWidth: 'min(90vw, 40em)', minHeight: 'calc(100vh - 9em)' }
32
- },
33
- Content() {
34
- return h(FilePicker, {
35
- multiple: false,
36
- folders,
37
- files,
38
- fileMask,
39
- from: value || defaultPath,
40
- async onSelect(sel) {
41
- let one = sel?.[0]
42
- if (!one) return
43
- const cwd = (await apiCall('get_cwd'))?.path
44
- if (one.startsWith(cwd))
45
- one = one.slice(cwd.length+1) || '.'
46
- onChange(one, { was: value, event: 'picker' })
47
- close()
48
- }
49
- })
50
- },
19
+ InputProps: { multiline: true },
20
+ end: h(IconBtn, {
21
+ icon: Eject,
22
+ title: "Browse files...",
23
+ edge: 'end',
24
+ sx: { mb: .5 },
25
+ onClick() {
26
+ const { close } = newDialog({
27
+ title: title ?? (files ? "Pick a file" : "Pick a folder"),
28
+ dialogProps: {
29
+ fullScreen: !large,
30
+ sx: { minWidth: 'min(90vw, 40em)', minHeight: 'calc(100vh - 9em)' }
31
+ },
32
+ Content() {
33
+ return h(FilePicker, {
34
+ multiple: false,
35
+ folders,
36
+ files,
37
+ fileMask,
38
+ from: value || defaultPath,
39
+ async onSelect(sel) {
40
+ let one = sel?.[0]
41
+ if (!one) return
42
+ const cwd = (await apiCall('get_cwd'))?.path
43
+ if (one.startsWith(cwd))
44
+ one = one.slice(cwd.length+1) || '.'
45
+ onChange(one, { was: value, event: 'picker' })
46
+ close()
47
+ }
48
})
49
},
53
- }))
54
- }
50
+ })
51
+ },
52
+ })
53
})
54
}
admin/src/FilePicker.ts
+34
-31
@@ -55,37 +55,40 @@ export default function FilePicker({ onSelect, multiple=true, files=true, folder
55
const cwdDelimiter = enforceFinal(pathDelimiter, cwd)
56
const isRoot = cwd.length < 2
57
return h(Fragment, {},
58
- h(Box, { display: 'flex', gap: 1 },
59
- h(Button, {
60
- title: "root",
61
- disabled: isRoot,
62
- onClick() {
63
- setCwd(root)
64
- }
65
- }, h(VerticalAlignTop)),
66
- h(Button, {
67
- disabled: isRoot,
68
- title: "parent folder",
69
- onClick() {
70
- const cwdND = /[\\/]$/.test(cwd) ? cwd.slice(0,-1) : cwd // exclude final delimiter, if any
71
- const parent = isWindowsDrive(cwdND) ? root : cwdND.slice(0, cwdND.lastIndexOf(pathDelimiter) || 1)
72
- setCwd(parent)
73
- }
74
- }, h(ArrowUpward)),
75
- h(StringField, {
76
- label: "Current path",
77
- value: cwd,
78
- InputLabelProps: { shrink: true },
79
- async onChange(v) {
80
- if (!v)
81
- return setCwd(root)
82
- const res = await apiCall('resolve_path', { path: v })
83
- if (res.isFolder === false)
84
- return files ? onSelect([v]) : setCwd(v.slice(0, -basename(v)))
85
- setCwd(res.path)
86
- },
87
- }),
88
- ),
58
+ h(StringField, {
59
+ label: "Current folder",
60
+ value: cwd,
61
+ InputLabelProps: { shrink: true },
62
+ helperText: "UNC paths are supported",
63
+ async onChange(v) {
64
+ if (!v)
65
+ return setCwd(root)
66
+ const res = await apiCall('resolve_path', { path: v })
67
+ if (res.isFolder === false)
68
+ return files ? onSelect([v]) : setCwd(v.slice(0, -basename(v)))
69
+ setCwd(res.path)
70
+ },
71
+ end: [
72
+ h(IconBtn, {
73
+ title: "root",
74
+ disabled: isRoot,
75
+ icon: VerticalAlignTop,
76
+ onClick() {
77
+ setCwd(root)
78
+ }
79
+ }),
80
+ h(IconBtn, {
81
+ title: "parent folder",
82
+ disabled: isRoot,
83
+ icon: ArrowUpward,
84
+ onClick() {
85
+ const cwdND = /[\\/]$/.test(cwd) ? cwd.slice(0,-1) : cwd // exclude final delimiter, if any
86
+ const parent = isWindowsDrive(cwdND) ? root : cwdND.slice(0, cwdND.lastIndexOf(pathDelimiter) || 1)
87
+ setCwd(parent)
88
+ }
89
+ }),
90
+ ]
91
+ }),
92
error ? h(Alert, { severity: 'error' }, err2msg(error))
93
: h(Fragment, {},
94
h(Box, {
admin/src/HomePage.ts
+1
@@ -75,6 +75,7 @@ export default function HomePage() {
75
SOLUTION_SEP, cfgLink("set the number of proxies"),
76
SOLUTION_SEP, "unless you are sure and you can ", h(Button, {
77
size: 'small',
78
+ sx: { lineHeight: 'unset' }, // fit in the line, avoiding bad layout
79
async onClick() {
80
if (await confirmDialog("Go on only if you know what you are doing")
81
&& await apiCall('set_config', { values: { ignore_proxies: true } }))