fix: admin/fs: inconsistent behavior of root button under linux

Massimo Melina committed Jul 26, 2022 at 11:25 UTC 28f601423ce52d191da5ee2a84d7085cfa55a0eb
1 file changed +33 -17
admin/src/FilePicker.ts
+33 -17
@@ -1,6 +1,6 @@
1 // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 -import { createElement as h, Fragment, useEffect, useMemo, useState } from 'react'
3 +import { createElement as h, Fragment, useEffect, useMemo, useRef, useState } from 'react'
4 import { apiCall, useApiList } from './api'
5 import _ from 'lodash'
6 import {
@@ -15,7 +15,7 @@ import {
15 Typography
16 } from '@mui/material'
17 import { enforceFinal, formatBytes, isWindowsDrive, spinner, Center, err2msg } from './misc'
18 -import { ArrowUpward, Home } from '@mui/icons-material'
18 +import { ArrowUpward, VerticalAlignTop } from '@mui/icons-material'
19 import { StringField } from '@hfs/mui-grid-form'
20 import { FileIcon, FolderIcon } from './VfsTree'
21 import { FixedSizeList } from 'react-window'
@@ -34,10 +34,13 @@ interface FilePickerProps {
34 export default function FilePicker({ onSelect, multiple=true, files=true, folders=true, fileMask, from='' }: FilePickerProps) {
35 const [cwd, setCwd] = useState(from)
36 const [ready, setReady] = useState(false)
37 + const isWindows = useRef(false)
38 useEffect(() => {
39 apiCall('resolve_path', { path: from, closestFolder: true }).then(res => {
39 - if (typeof res.path === 'string')
40 + if (typeof res.path === 'string') {
41 setCwd(res.path)
42 + isWindows.current = res.path[1] === ':'
43 + }
44 }).finally(() => setReady(true))
45 }, [from])
46 const { list, error, loading } = useApiList<DirEntry>(ready && 'ls', { path: cwd, files, fileMask })
@@ -57,28 +60,41 @@ export default function FilePicker({ onSelect, multiple=true, files=true, folder
60 const filteredList = useMemo(() => list.filter(it => filterMatch(it.n)), [list,filterMatch])
61 if (loading)
62 return spinner()
60 - const pathDelimiter = /[:\\]/.test(cwd) ? '\\' : '/'
61 - const cwdPostfixed = enforceFinal(pathDelimiter, cwd)
63 + const root = isWindows.current ? '' : '/'
64 + const pathDelimiter = isWindows.current ? '\\' : '/'
65 + const cwdDelimiter = enforceFinal(pathDelimiter, cwd)
66 + const isRoot = cwd.length < 2
67 return h(Fragment, {},
63 - h(Box, { display:'flex', gap: 1 },
68 + h(Box, { display: 'flex', gap: 1 },
69 h(Button, {
70 + title: "root",
71 + disabled: isRoot,
72 onClick() {
66 - const s = /[\\/]$/.test(cwd) ? cwd.slice(0,-1) : cwd // exclude final delimiter, if any
67 - setCwd( isWindowsDrive(s) ? '' : s.slice(0, s.lastIndexOf(pathDelimiter)) )
73 + setCwd(root)
74 }
69 - }, h(ArrowUpward)),
75 + }, h(VerticalAlignTop)),
76 h(Button, {
77 + disabled: isRoot,
78 + title: "parent folder",
79 onClick() {
72 - setCwd('')
80 + const cwdND = /[\\/]$/.test(cwd) ? cwd.slice(0,-1) : cwd // exclude final delimiter, if any
81 + const parent = isWindowsDrive(cwdND) ? root : cwdND.slice(0, cwdND.lastIndexOf(pathDelimiter) || 1)
82 + setCwd(parent)
83 }
74 - }, h(Home)),
84 + }, h(ArrowUpward)),
85 h(StringField, {
76 - label: 'Current path',
86 + label: "Current path",
87 value: cwd,
78 - onChange: setCwd as any,
88 + InputLabelProps: { shrink: true },
89 + async onChange(v) {
90 + if (!v)
91 + return setCwd(root)
92 + const res = await apiCall('resolve_path', { path: v })
93 + setCwd(res.path)
94 + },
95 }),
96 ),
81 - error ? h(Alert, { severity:'error' }, err2msg(error))
97 + error ? h(Alert, { severity: 'error' }, err2msg(error))
98 : h(Fragment, {},
99 h(Box, { sx: { flex: 1 } },
100 !list.length ? h(Center, { flex: 1, mt: '4em' }, "No elements in this folder") : h(AutoSizer, {
@@ -93,9 +109,9 @@ export default function FilePicker({ onSelect, multiple=true, files=true, folder
109 key: it.n,
110 onClick() {
111 if (isFolder)
96 - setCwd(cwdPostfixed + it.n)
112 + setCwd(cwdDelimiter + it.n)
113 else
98 - onSelect([cwdPostfixed + it.n])
114 + onSelect([cwdDelimiter + it.n])
115 }
116 },
117 multiple && h(Checkbox, {
@@ -126,7 +142,7 @@ export default function FilePicker({ onSelect, multiple=true, files=true, folder
142 disabled: !folders && !sel.length && files,
143 sx: { minWidth: 'max-content' },
144 onClick() {
129 - onSelect(sel.length ? sel.map(x => cwdPostfixed + x) : [cwd])
145 + onSelect(sel.length ? sel.map(x => cwdDelimiter + x) : [cwd])
146 }
147 }, files && (sel.length || !folders) ? `Select (${sel.length})` : `Select this folder`),
148 h(TextField, {