fix: (regression 0.57.15) admin/shared: collapse-all button not working
Massimo Melina committed
Sep 28, 2025 at 19:28 UTC
daafa0b8864651bf55251ee94505626dcc48fbf4
2 files changed
+17
-11
admin/src/VfsPage.ts
+2
-2
@@ -159,7 +159,7 @@ export default function VfsPage({ setTitleSide }: PageProps) {
159
const scrollProps = { height: '100%', display: 'flex', flexDirection: 'column', overflow: 'auto' } as const
160
return h(Grid, { container: true, rowSpacing: 1, columnSpacing: 2, top: 0, flex: '1 1 auto', height: 0 },
161
h(Grid, { item: true, xs: 12, [sideBreakpoint]: 5, lg: 6, xl: 5, ...scrollProps },
162
- h(VfsTree, { id2node, statusApi }) ),
162
+ id2node.size > 0 && h(VfsTree, { id2node, statusApi }) ),
163
isSideBreakpoint && sideContent && h(Grid, { item: true, [sideBreakpoint]: true, maxWidth: '100%', ...scrollProps },
164
h(Card, { sx: { overflow: 'initial' } }, // overflow is incompatible with stickyBar
165
h(CardContent, {}, sideContent)) )
@@ -181,7 +181,7 @@ export async function deleteFiles() {
181
&& _.findLastIndex(uris, y => x.startsWith(y), i - 1) !== -1) // search backward among previous elements, as they array is sorted
182
_.pull(uris, '/')
183
const { errors } = await apiCall('del_vfs', { uris })
184
- const urisThatFailed = uris.filter((uri, idx) => errors[idx])
184
+ const urisThatFailed = uris.filter((_uri, idx) => errors[idx])
185
if (urisThatFailed.length)
186
return alertDialog("Following elements couldn't be deleted: " + urisThatFailed.join(', '), 'error')
187
reloadVfs()
admin/src/VfsTree.ts
+15
-9
@@ -9,7 +9,7 @@ import {
9
} from '@mui/icons-material'
10
import { Box, Typography } from '@mui/material'
11
import { reloadVfs, VfsNode } from './VfsPage'
12
-import { onlyTruthy, toMutable, useEffectOnce, Who, with_ } from './misc'
12
+import { onlyTruthy, toMutable, Who, with_ } from './misc'
13
import { Flex, iconTooltip, useToggleButton } from './mui'
14
import VfsMenuBar from './VfsMenuBar'
15
import { apiCall, ApiObject } from './api'
@@ -19,6 +19,8 @@ import _ from 'lodash'
19
export const FolderIcon = Folder
20
export const FileIcon = InsertDriveFileOutlined
21
22
+let once = true
23
+
24
export default function VfsTree({ id2node, statusApi }:{ id2node: Map<string, VfsNode>, statusApi: ApiObject }) {
25
const { vfs, selectedFiles, expanded } = useSnapState()
26
const dragging = useRef<string>()
@@ -106,15 +108,19 @@ export default function VfsTree({ id2node, statusApi }:{ id2node: Map<string, Vf
108
}
109
}, [statusApi.data])
110
const ref = useRef<HTMLUListElement>(null)
109
- const [expandAll, toggleBtn] = useToggleButton("Collapse all", "Expand all", exp => ({
111
+ const allExpanded = expanded.length === id2node.size
112
+ const initialExpansion = ['/', ...vfs?.children?.length === 1 ? [vfs.children[0].id] : []] // in case there's only one child, expand that too
113
+ if (once) {
114
+ once = false
115
+ state.expanded = initialExpansion
116
+ }
117
+ const [_expandAll, toggleBtn] = useToggleButton("Collapse all", "Expand all", exp => ({
118
icon: exp ? UnfoldLess : UnfoldMore,
119
sx: { rotate: exp ? 0 : '180deg' },
112
- }), expanded.length === id2node.size)
113
- useEffectOnce(() => { // this is also resetting the state at each mount
114
- state.expanded = expandAll ? Array.from(id2node.keys())
115
- : state.expanded.length ? state.expanded // keep the previous state
116
- : ['/', ...vfs?.children?.length === 1 ? [vfs.children[0].id] : []] // in case there's only one child, expand that too
117
- }, [expandAll, Boolean(vfs)]) // vfs is undefined on the first render, we want to be called again as soon as it is loaded first time and not at reloads
120
+ onClick() {
121
+ state.expanded = allExpanded ? initialExpansion : Array.from(id2node.keys())
122
+ }
123
+ }), allExpanded)
124
useEffect(() => {
125
state.expanded = _.uniq(state.expanded.concat(state.selectedFiles.map(x => x.parent?.id || '')))
126
}, [state.vfs])
@@ -140,7 +146,7 @@ export default function VfsTree({ id2node, statusApi }:{ id2node: Map<string, Vf
146
maxWidth: ref.current && `calc(100vw - ${16 + ref.current.offsetLeft}px)`, // limit possible horizontal scrolling to this element
147
'& ul': { borderLeft: '1px dashed #444', marginLeft: '15px', paddingLeft: '15px' },
148
},
143
- onNodeSelect(ev, ids) {
149
+ onNodeSelect(_ev, ids) {
150
if (typeof ids === 'string') return // shut up ts
151
state.selectedFiles = onlyTruthy(ids.map(id => id2node.get(id)))
152
}