Spaces:
Running
Running
Download src/CodePanel.tsx from fffiloni/Sand3D-assets-explorer: direct link, hf CLI and curl.
- Browser
- Download file 3.44 kB
-
https://huggingface.co/spaces/fffiloni/Sand3D-assets-explorer/resolve/main/src/CodePanel.tsx
- Command line
-
hf download hf://spaces/fffiloni/Sand3D-assets-explorer/src/CodePanel.tsx
-
curl -L -o CodePanel.tsx https://huggingface.co/spaces/fffiloni/Sand3D-assets-explorer/resolve/main/src/CodePanel.tsx
3.44 kB
| import {useEffect,useMemo,useRef,useState} from 'react'; | |
| import {createPortal} from 'react-dom'; | |
| import {Check,Copy,Download,Maximize2,Minimize2,X} from 'lucide-react'; | |
| import {highlightPython} from './python-highlight'; | |
| type Props={code:string;error:string;url:string;id:string;version:string;onClose:()=>void}; | |
| export default function CodePanel({code,error,url,id,version,onClose}:Props){ | |
| const [copied,setCopied]=useState(false),[copyError,setCopyError]=useState(false),[expanded,setExpanded]=useState(false); | |
| const panel=useRef<HTMLElement>(null),expandButton=useRef<HTMLButtonElement>(null); | |
| const collapse=()=>{setExpanded(false);requestAnimationFrame(()=>expandButton.current?.focus());}; | |
| useEffect(()=>{ | |
| if(!expanded)return; | |
| const app=document.querySelector<HTMLElement>('.app-shell'),wasInert=app?.inert; | |
| if(app)app.inert=true; | |
| panel.current?.querySelector<HTMLElement>('pre,button')?.focus(); | |
| const key=(e:KeyboardEvent)=>{ | |
| if(e.key==='Escape'){e.preventDefault();collapse();} | |
| if(e.key==='Tab'){ | |
| const nodes=Array.from(panel.current?.querySelectorAll<HTMLElement>('button:not(:disabled),a[href],pre[tabindex]')??[]),first=nodes[0],last=nodes.at(-1); | |
| if(e.shiftKey&&document.activeElement===first){e.preventDefault();last?.focus();} | |
| else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first?.focus();} | |
| } | |
| }; | |
| document.addEventListener('keydown',key);return()=>{document.removeEventListener('keydown',key);if(app)app.inert=wasInert??false;}; | |
| },[expanded]); | |
| const html=useMemo(()=>highlightPython(code),[code]); | |
| const copy=async()=>{try{await navigator.clipboard.writeText(code);setCopied(true);setCopyError(false);}catch{setCopied(false);setCopyError(true);}}; | |
| const content=<section ref={panel} id="selection-details" className={`dock-panel selection-details code-panel ${expanded?'code-expanded':''}`} aria-label="Python program" role={expanded?'dialog':undefined} aria-modal={expanded||undefined} data-ui> | |
| <header className="code-heading"><div><h2>The program</h2><p>Python · sand3d {version}{code?` · ${code.split('\n').length} lines`:''}</p></div> | |
| <div className="code-actions"> | |
| <button className="icon-button" aria-label={copied?'Python copied':'Copy Python'} title={copied?'Copied':'Copy Python'} disabled={!code} onClick={copy}>{copied?<Check size={16}/>:<Copy size={16}/>}</button> | |
| <a className="icon-button" aria-label="Download Python" title="Download Python" href={url} download={`${id}.py`}><Download size={16}/></a> | |
| <button ref={expandButton} className="icon-button" aria-label={expanded?'Collapse code':'Expand code'} title={expanded?'Collapse code':'Expand code'} onClick={()=>expanded?collapse():setExpanded(true)}>{expanded?<Minimize2 size={16}/>:<Maximize2 size={16}/>}</button> | |
| <button className="icon-button" aria-label="Close Python program" title="Close Python program" onClick={onClose}><X size={16}/></button> | |
| </div> | |
| </header> | |
| {copyError&&<p role="alert">Clipboard unavailable. Select the code below to copy it.</p>} | |
| {error?<p role="alert">{error}</p>:code?<pre tabIndex={0} aria-label="Python source code"><span className="code-gutter" aria-hidden="true">{code.split('\n').map((_,i)=>i+1).join('\n')}</span><code dangerouslySetInnerHTML={{__html:html}}/></pre>:<p role="status">Loading program…</p>} | |
| </section>; | |
| return expanded?createPortal(<div className="code-scrim">{content}</div>,document.body):content; | |
| } | |