aboutsummaryrefslogtreecommitdiff
path: root/src/components/SidePanel/SidePanel.tsx
diff options
context:
space:
mode:
authorzwlucas <lucas.fariamo08@gmail.com>2026-06-05 20:52:54 +0000
committerzwlucas <lucas.fariamo08@gmail.com>2026-06-05 20:52:54 +0000
commit09f964451d7d92e9891430ec4595c1276d486aab (patch)
tree28da4483f5c28924a8c47fceb648b1baebe88224 /src/components/SidePanel/SidePanel.tsx
downloadyace-master.tar.gz
yace-master.zip
feat: uploadHEADmaster
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'src/components/SidePanel/SidePanel.tsx')
-rw-r--r--src/components/SidePanel/SidePanel.tsx187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/components/SidePanel/SidePanel.tsx b/src/components/SidePanel/SidePanel.tsx
new file mode 100644
index 0000000..14668fb
--- /dev/null
+++ b/src/components/SidePanel/SidePanel.tsx
@@ -0,0 +1,187 @@
+import { useRef, useCallback, useEffect } from 'react'
+import FileTree from '@/components/Sidebar/FileTree'
+import SettingsPanel from '@/components/Settings/SettingsPanel'
+import { useFilesStore } from '@/stores/useFilesStore'
+import { useTabsStore } from '@/stores/useTabsStore'
+import { useLayoutStore } from '@/stores/useLayoutStore'
+
+function NewFileIcon() {
+ return (
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
+ <polyline points="14 2 14 8 20 8" />
+ <line x1="12" y1="18" x2="12" y2="12" />
+ <line x1="9" y1="15" x2="15" y2="15" />
+ </svg>
+ )
+}
+
+function NewFolderIcon() {
+ return (
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
+ <line x1="12" y1="11" x2="12" y2="17" />
+ <line x1="9" y1="14" x2="15" y2="14" />
+ </svg>
+ )
+}
+
+function OpenFolderIcon() {
+ return (
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
+ </svg>
+ )
+}
+
+export default function SidePanel() {
+ const fileTree = useFilesStore((s) => s.fileTree)
+ const rootPath = useFilesStore((s) => s.rootPath)
+ const loading = useFilesStore((s) => s.loading)
+ const openFolder = useFilesStore((s) => s.openFolder)
+ const createFolder = useFilesStore((s) => s.createFolder)
+ const createUntitledFile = useTabsStore((s) => s.createUntitledFile)
+
+ const sidePanelWidth = useLayoutStore((s) => s.sidePanelWidth)
+ const setSidePanelWidth = useLayoutStore((s) => s.setSidePanelWidth)
+ const activeView = useLayoutStore((s) => s.activeView)
+
+ const dragRef = useRef(false)
+ const panelRef = useRef<HTMLDivElement>(null)
+
+ const handleMouseDown = useCallback(() => {
+ dragRef.current = true
+ document.body.style.cursor = 'col-resize'
+ document.body.style.userSelect = 'none'
+ }, [])
+
+ const handleMouseMove = useCallback((e: MouseEvent) => {
+ if (!dragRef.current || !panelRef.current) return
+ const rect = panelRef.current.getBoundingClientRect()
+ const newWidth = e.clientX - rect.left
+ setSidePanelWidth(newWidth)
+ }, [setSidePanelWidth])
+
+ const handleMouseUp = useCallback(() => {
+ dragRef.current = false
+ document.body.style.cursor = ''
+ document.body.style.userSelect = ''
+ }, [])
+
+ useEffect(() => {
+ window.addEventListener('mousemove', handleMouseMove)
+ window.addEventListener('mouseup', handleMouseUp)
+ return () => {
+ window.removeEventListener('mousemove', handleMouseMove)
+ window.removeEventListener('mouseup', handleMouseUp)
+ }
+ }, [handleMouseMove, handleMouseUp])
+
+ return (
+ <div
+ ref={panelRef}
+ className="relative flex flex-col h-full panel-mica select-none shrink-0 overflow-hidden"
+ style={{ width: `${sidePanelWidth}px` }}
+ role="complementary"
+ aria-label="Side panel"
+ >
+ <div className="group flex items-center justify-between px-3 shrink-0 h-tabs border-b border-panel-border">
+ <span className="uppercase tracking-widest text-[11px] font-medium text-zinc-500">
+ {activeView === 'search' ? 'SEARCH' : activeView === 'extensions' ? 'EXTENSIONS' : activeView === 'settings' ? 'SETTINGS' : 'EXPLORER'}
+ </span>
+ <div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity duration-fast">
+ {activeView === 'explorer' && (
+ <>
+ <button
+ onClick={() => createUntitledFile()}
+ className="p-1 rounded text-zinc-500 hover:text-zinc-300 hover:bg-panel-hover transition-all duration-fast"
+ title="New File"
+ >
+ <NewFileIcon />
+ </button>
+ {rootPath && (
+ <button
+ onClick={() => createFolder()}
+ className="p-1 rounded text-zinc-500 hover:text-zinc-300 hover:bg-panel-hover transition-all duration-fast"
+ title="New Folder"
+ >
+ <NewFolderIcon />
+ </button>
+ )}
+ <button
+ onClick={openFolder}
+ className="p-1 rounded text-zinc-500 hover:text-zinc-300 hover:bg-panel-hover transition-all duration-fast"
+ title={rootPath ? 'Open another folder' : 'Open Folder'}
+ >
+ <OpenFolderIcon />
+ </button>
+ </>
+ )}
+ </div>
+ </div>
+
+ <div className="flex-1 overflow-y-auto overflow-x-hidden">
+ {activeView === 'explorer' && (
+ <>
+ {!rootPath && !loading && (
+ <div className="flex flex-col items-center gap-3 px-4 py-8 text-center">
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className="text-text-muted">
+ <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
+ </svg>
+ <p className="text-ui-sm text-text-secondary">No folder open</p>
+ <button
+ onClick={openFolder}
+ className="px-3 py-1.5 text-ui-xs font-medium rounded transition-all duration-150 hover:brightness-110 bg-accent text-editor-bg"
+ >
+ Open Folder
+ </button>
+ </div>
+ )}
+
+ {loading && (
+ <div className="flex items-center justify-center gap-2 py-8">
+ <div className="w-3 h-3 rounded-full animate-pulse bg-accent" />
+ <span className="text-ui-sm text-text-secondary">Loading...</span>
+ </div>
+ )}
+
+ {rootPath && !loading && (
+ <>
+ <div className="px-3 py-1.5 text-ui-xs font-medium truncate text-text-muted border-b border-panel-border" title={rootPath}>
+ {rootPath.split(/[\\/]/).pop()}
+ </div>
+ {fileTree.length > 0 ? (
+ <FileTree entries={fileTree} />
+ ) : (
+ <div className="py-8 text-ui-sm text-text-secondary text-center">Empty directory</div>
+ )}
+ </>
+ )}
+ </>
+ )}
+
+ {activeView === 'search' && (
+ <div className="flex items-center justify-center py-8 text-ui-sm text-text-muted">
+ Search coming soon
+ </div>
+ )}
+
+ {activeView === 'extensions' && (
+ <div className="flex items-center justify-center py-8 text-ui-sm text-text-muted">
+ Extensions coming soon
+ </div>
+ )}
+
+ {activeView === 'settings' && (
+ <SettingsPanel />
+ )}
+ </div>
+
+ <div
+ className="absolute right-0 top-0 bottom-0 w-[4px] cursor-col-resize hover:bg-accent/30 transition-colors duration-fast"
+ style={{ right: '-2px' }}
+ onMouseDown={handleMouseDown}
+ />
+ </div>
+ )
+}