Show complete folder names in file sidebar tree

This commit is contained in:
toorux committed 2026-10-07 18:44:54 +08:00
1 parent 6a0e302fd6
commit 79db4a80fc
3 files changed
+17 -2

No files matched your search

+12 -1
View File
@@ -7,6 +7,7 @@ import '../ui/src/style.css';
const wait=(ms=40)=>new Promise(r=>setTimeout(r,ms));
const until=async(f:()=>unknown)=>{for(let n=0;n<150;n++){if(f())return;await wait();}throw Error('Timeout '+document.body.innerText.slice(-800));};
const home='/home/steamos',downloads=home+'/Downloads';
const longFolder='一个很长的文件夹名称 - Project documents and archived screenshots';
let preferences:any={view:'list',folders:{[home]:{sort:'size',descending:true},[downloads]:{sort:'modified',descending:false}}};
const calls:any[]=[];
const files=(directory:string)=>[
@@ -26,7 +27,10 @@ window.fetch=async(url,init)=>{
}else if(p.operation==='list'){
let directory=p.path||home;if(directory==='/alias')directory=home;
let entries=files(directory);
if(p.hidden)entries=[]; // Tree requests do not consume the sort checks below.
if(p.hidden){ // Tree requests do not consume the sort checks below.
const child=directory==='/'?{name:'home',path:'/home'}:directory==='/home'?{name:'steamos',path:home}:directory===home?{name:longFolder,path:home+'/'+longFolder}:null;
entries=child?[{...child,directory:true,symlink:false,size:0,mode:493,uid:1000,gid:1000}]:[];
}
result={path:directory,parent:'/',entries,total:entries.length,roots:[{name:'Home',path:home},{name:'Downloads',path:downloads}]};
}else if(p.operation==='read')result={text:'fixture text',revision:'revision'};
else throw Error('Unexpected operation '+p.operation);
@@ -42,6 +46,13 @@ const changeAddress=async(value:string)=>{const input=document.querySelector<HTM
(window as any).runInstallReviewChecks=async()=>{try{
configureLanguage('en-US',[]);render('one');await until(ready);
if(listing().sort!=='size'||listing().descending!==true)throw Error('Home sort not restored on initial canonical path');
await until(()=>[...document.querySelectorAll('.file-tree-entry button>span')].some(node=>node.textContent===longFolder));
const tree=document.querySelector<HTMLElement>('.file-tree-scroll')!,name=[...tree.querySelectorAll<HTMLElement>('button>span')].find(node=>node.textContent===longFolder)!;
if(getComputedStyle(name).textOverflow==='ellipsis'||name.scrollWidth>name.clientWidth+1)throw Error('Tree folder name clipped');
if(tree.scrollWidth<=tree.clientWidth)throw Error('Tree does not expose horizontal overflow');
tree.scrollLeft=tree.scrollWidth;if(tree.scrollLeft<=0)throw Error('Cannot scroll long tree names');tree.scrollLeft=0;
if(name.closest('button')?.title!==home+'/'+longFolder)throw Error('Full path tooltip missing');
await select('View','Large icons');await until(()=>document.querySelector('.file-icon-grid.icons-large'));
if(document.querySelector('.file-table'))throw Error('Table remained in icon view');
const checkbox=document.querySelector<HTMLInputElement>('.file-tile-check input')!;checkbox.click();await until(()=>document.querySelector('.file-icon-tile.selected'));
+4
View File
@@ -31,6 +31,10 @@
.file-manager .file-tree-entry{gap:0;min-height:38px}
.file-manager .file-tree-entry>button.file-tree-toggle{flex:none;padding:4px;width:26px;min-width:26px;border:0}
.file-manager .file-tree-entry>button:last-child{width:auto;flex:1;min-width:0;padding-left:4px;gap:8px}
/* Keep complete tree names on one line; the tree scrolls horizontally. */
.file-manager .file-tree-scroll>div{width:max-content;min-width:100%}
.file-manager .file-tree-entry>button:last-child{flex:1 0 auto;min-width:max-content}
.file-manager .file-tree-entry>button>span{overflow:visible;text-overflow:clip;white-space:nowrap;flex:none}
.file-manager .file-directory-tree .file-tree{padding-left:12px;border-left:1px solid #ffffff0a;margin-left:12px}
.file-manager .file-side-entry{display:flex;align-items:center;min-width:0;position:relative}
.file-manager .file-side-entry>button:first-child{min-width:0}
+1 -1
View File
@@ -29,7 +29,7 @@ function FolderTree({name,path,onOpen,current,depth=0}:{name:string;path:string;
}catch(e){if(id===request.current){setError(String(e));setExpanded(true);}}}
useEffect(()=>{if(depth<20&&(depth===0||ancestor||current===path))void expand(0,ancestor);return()=>{request.current++;};},[path,current]);
useEffect(()=>{if(current!==path||!row.current)return;const node=row.current,sidebar=node.closest<HTMLElement>('.file-tree-scroll');if(!sidebar)return;const a=node.getBoundingClientRect(),b=sidebar.getBoundingClientRect();if(a.top<b.top)sidebar.scrollTop+=a.top-b.top-8;else if(a.bottom>b.bottom)sidebar.scrollTop+=a.bottom-b.bottom+8;},[current,path]);
return <div><div ref={row} className="file-side-entry file-tree-entry"><button className="file-tree-toggle" disabled={depth>=20} aria-expanded={expanded} aria-label={t(expanded?'收起文件夹':'展开文件夹')} onClick={()=>{if(expanded){request.current++;setExpanded(false);}else void expand();}}>{expanded?<IconChevronDown size={16}/>:<IconChevronRight size={16}/>}</button><button aria-current={current===path?'location':undefined} className={current===path?'active':''} onClick={()=>onOpen(path)}><IconFolder size={18}/><span>{name}</span></button></div>{error&&expanded&&<small>{error}</small>}{expanded&&<div className="file-tree">{children.map(e=><FolderTree key={e.path} name={e.name} path={e.path} current={current} onOpen={onOpen} depth={depth+1}/>)}{more&&<button onClick={()=>void expand(offset)}>{t('加载更多')}</button>}</div>}</div>;
return <div><div ref={row} className="file-side-entry file-tree-entry"><button className="file-tree-toggle" disabled={depth>=20} aria-expanded={expanded} aria-label={t(expanded?'收起文件夹':'展开文件夹')} onClick={()=>{if(expanded){request.current++;setExpanded(false);}else void expand();}}>{expanded?<IconChevronDown size={16}/>:<IconChevronRight size={16}/>}</button><button title={path} aria-current={current===path?'location':undefined} className={current===path?'active':''} onClick={()=>onOpen(path)}><IconFolder size={18}/><span>{name}</span></button></div>{error&&expanded&&<small>{error}</small>}{expanded&&<div className="file-tree">{children.map(e=><FolderTree key={e.path} name={e.name} path={e.path} current={current} onOpen={onOpen} depth={depth+1}/>)}{more&&<button onClick={()=>void expand(offset)}>{t('加载更多')}</button>}</div>}</div>;
}
export function FileManager({active=true}:{active?:boolean}){
const[data,setData]=useState<Listing>({path:'',entries:[],total:0,roots:[]}),[path,setPath]=useState(''),[address,setAddress]=useState(''),[query,setQuery]=useState(''),[hidden,setHidden]=useState(false),[offset,setOffset]=useState(0),[selected,setSelected]=useState<string[]>([]),[bookmarks,setBookmarks]=useState<Bookmark[]>([]),[error,setError]=useState(''),[busy,setBusy]=useState(false),[loading,setLoading]=useState(true),[dialog,setDialog]=useState<Dialog|null>(null),[value,setValue]=useState(''),[policy,setPolicy]=useState('keep'),[permanent,setPermanent]=useState(false),[job,setJob]=useState<Job|null>(null),[clipboard,setClipboard]=useState<{paths:string[];cut:boolean}|null>(null),[preview,setPreview]=useState<{entry:Entry;url:string;video:boolean}|null>(null),[editor,setEditor]=useState<{path:string;text:string;revision:string}|null>(null),[modified,setModified]=useState(false),[trash,setTrash]=useState<any[]|null>(null),[upload,setUpload]=useState<{name:string;received:number;total:number}|null>(null),[results,setResults]=useState<any[]|null>(null),[counter,setCounter]=useState(0),[context,setContext]=useState(false),[bookmarkMenu,setBookmarkMenu]=useState<string|null>(null),[outcomes,setOutcomes]=useState<any[]>([]),[menu,setMenu]=useState<{x:number;y:number;blank:boolean}|null>(null);