@samitouri / QOSami-HFS / commits / d774776a

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 } }))