From 0048eabb606ad514b4189ac67f3ec6c9d7b14e6b Mon Sep 17 00:00:00 2001 From: Butterfly Dev Date: Tue, 7 Apr 2026 03:35:30 +0000 Subject: [PATCH] =?UTF-8?q?desktop:=20components/apps/file-explorer=20?= =?UTF-8?q?=E2=80=94=20dual-pane=20file=20browser=20with=20navigation,=20i?= =?UTF-8?q?cons,=20selection,=20status=20bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../file-explorer.component.html | 39 ++++++ .../file-explorer.component.scss | 124 ++++++++++++++++++ .../file-explorer/file-explorer.component.ts | 53 ++++++++ 3 files changed, 216 insertions(+) create mode 100644 desktop/src/app/components/apps/file-explorer/file-explorer.component.html create mode 100644 desktop/src/app/components/apps/file-explorer/file-explorer.component.scss create mode 100644 desktop/src/app/components/apps/file-explorer/file-explorer.component.ts diff --git a/desktop/src/app/components/apps/file-explorer/file-explorer.component.html b/desktop/src/app/components/apps/file-explorer/file-explorer.component.html new file mode 100644 index 0000000..c6f6dd7 --- /dev/null +++ b/desktop/src/app/components/apps/file-explorer/file-explorer.component.html @@ -0,0 +1,39 @@ +
+ +
+ + +
+ {{ currentPath() }} +
+
+ + +
+ @for (file of files(); track file.name) { +
+ {{ file.type === 'folder' ? '📁' : '📄' }} + {{ file.name }} + @if (file.size) { + {{ file.size }} + } + {{ file.modified }} +
+ } +
+ + +
+ {{ files().length }} items + @if (selectedFile()) { + {{ selectedFile() }} + } +
+
diff --git a/desktop/src/app/components/apps/file-explorer/file-explorer.component.scss b/desktop/src/app/components/apps/file-explorer/file-explorer.component.scss new file mode 100644 index 0000000..387cbc8 --- /dev/null +++ b/desktop/src/app/components/apps/file-explorer/file-explorer.component.scss @@ -0,0 +1,124 @@ +:host { + display: block; + width: 100%; + height: 100%; +} + +.file-explorer { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + background: #1e1e1e; +} + +.fe-toolbar { + display: flex; + align-items: center; + height: 36px; + padding: 0 8px; + background: #2d2d2d; + border-bottom: 1px solid #404040; + gap: 4px; +} + +.fe-btn { + width: 30px; + height: 28px; + border: none; + background: transparent; + color: #ccc; + cursor: pointer; + border-radius: 4px; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + background: rgba(255, 255, 255, 0.08); + } +} + +.fe-path { + flex: 1; + height: 26px; + margin-left: 8px; + display: flex; + align-items: center; + background: #383838; + border: 1px solid #555; + border-radius: 4px; + padding: 0 10px; +} + +.path-text { + font-size: 12px; + color: #ccc; +} + +.fe-content { + flex: 1; + overflow-y: auto; + padding: 4px; +} + +.fe-item { + display: flex; + align-items: center; + gap: 10px; + padding: 6px 12px; + border-radius: 4px; + cursor: default; + font-size: 13px; + color: #ddd; + + &:hover { + background: rgba(255, 255, 255, 0.04); + } + + &.selected { + background: rgba(0, 120, 212, 0.25); + } +} + +.fe-icon { + font-size: 16px; + flex-shrink: 0; +} + +.fe-name { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.fe-size { + font-size: 11px; + color: #888; + width: 70px; + text-align: right; + flex-shrink: 0; +} + +.fe-date { + font-size: 11px; + color: #888; + width: 90px; + text-align: right; + flex-shrink: 0; +} + +.fe-statusbar { + display: flex; + justify-content: space-between; + height: 24px; + padding: 0 12px; + background: #2d2d2d; + border-top: 1px solid #404040; + font-size: 11px; + color: #888; + align-items: center; +} diff --git a/desktop/src/app/components/apps/file-explorer/file-explorer.component.ts b/desktop/src/app/components/apps/file-explorer/file-explorer.component.ts new file mode 100644 index 0000000..4686cbc --- /dev/null +++ b/desktop/src/app/components/apps/file-explorer/file-explorer.component.ts @@ -0,0 +1,53 @@ +import { Component, signal } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +interface FileItem { + name: string; + type: 'folder' | 'file'; + size?: string; + modified?: string; +} + +@Component({ + selector: 'app-file-explorer', + standalone: true, + imports: [CommonModule], + templateUrl: './file-explorer.component.html', + styleUrl: './file-explorer.component.scss', +}) +export class FileExplorerComponent { + readonly currentPath = signal('/home/butterfly'); + readonly selectedFile = signal(null); + + readonly files = signal([ + { name: 'Documents', type: 'folder', modified: '2026-04-07' }, + { name: 'Downloads', type: 'folder', modified: '2026-04-07' }, + { name: 'Pictures', type: 'folder', modified: '2026-04-06' }, + { name: 'Desktop', type: 'folder', modified: '2026-04-07' }, + { name: 'readme.txt', type: 'file', size: '1.2 KB', modified: '2026-04-07' }, + { name: 'notes.md', type: 'file', size: '3.4 KB', modified: '2026-04-06' }, + { name: 'config.json', type: 'file', size: '512 B', modified: '2026-04-05' }, + ]); + + onSelect(file: FileItem): void { + this.selectedFile.set(file.name); + } + + onDoubleClick(file: FileItem): void { + if (file.type === 'folder') { + this.currentPath.set(this.currentPath() + '/' + file.name); + } + } + + goUp(): void { + const path = this.currentPath(); + const idx = path.lastIndexOf('/'); + if (idx > 0) { + this.currentPath.set(path.substring(0, idx)); + } + } + + goHome(): void { + this.currentPath.set('/home/butterfly'); + } +}