Skip to content

Commit

Permalink
feat: add FileTree component
Browse files Browse the repository at this point in the history
  • Loading branch information
lars-berger committed Oct 12, 2024
1 parent 9351343 commit 1cc8ef9
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
53 changes: 53 additions & 0 deletions packages/settings-ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,61 @@
import { FileItem, FileTree } from './FileTree';
import { WidgetSettings } from './WidgetSettings';

const initialFiles: FileItem[] = [
{
name: 'Documents',
type: 'folder',
children: [
{
name: 'Project Proposal.docx',
type: 'file',
size: '2.3 MB',
modified: '2023-10-05',
},
{
name: 'Budget.xlsx',
type: 'file',
size: '1.8 MB',
modified: '2023-10-06',
},
],
},
{
name: 'Images',
type: 'folder',
children: [
{
name: 'Vacation.jpg',
type: 'image',
size: '3.2 MB',
modified: '2023-09-28',
},
{
name: 'Family.png',
type: 'image',
size: '2.9 MB',
modified: '2023-09-29',
},
],
},
{
name: 'Music.mp3',
type: 'audio',
size: '5.4 MB',
modified: '2023-10-01',
},
{
name: 'Video.mp4',
type: 'video',
size: '15.7 MB',
modified: '2023-10-02',
},
];

export function App() {
return (
<div class="app">
<FileTree files={initialFiles} onSelect={() => {}} />
<WidgetSettings />
</div>
);
Expand Down
84 changes: 84 additions & 0 deletions packages/settings-ui/src/FileTree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
IconChevronDown,
IconChevronRight,
IconFile,
IconFolder,
} from '@glzr/components';
import { createSignal } from 'solid-js';

type FileType = 'file' | 'folder';

export interface FileItem {
name: string;
type: FileType;
size?: string;
modified?: string;
children?: FileItem[];
}

export interface FileTreeProps {
files: FileItem[];
onSelect: (file: FileItem) => void;
}

export function FileTree({ files, onSelect }: FileTreeProps) {
const [expanded, setExpanded] = createSignal<Record<string, boolean>>(
{},
);

const toggleExpand = (name: string) => {
setExpanded(prev => ({ ...prev, [name]: !prev[name] }));
};

const renderFileItem = (file: FileItem, level: number) => {
const isExpanded = expanded()[file.name];
const hasChildren = file.children && file.children.length > 0;

return (
<div class="select-none">
<div
class="flex items-center gap-1 px-2 py-1 hover:bg-gray-100 cursor-pointer"
style={{ 'padding-left': `${level * 16}px` }}
onClick={() => {
if (hasChildren) {
toggleExpand(file.name);
}
onSelect(file);
}}
>
{hasChildren && (
<button
onClick={() => toggleExpand(file.name)}
class="focus:outline-none"
>
{isExpanded ? (
<IconChevronDown class="w-4 h-4" />
) : (
<IconChevronRight class="w-4 h-4" />
)}
</button>
)}
<FileIcon type={file.type} />
<span>{file.name}</span>
</div>

{hasChildren && isExpanded && (
<div>
{file.children!.map(child => renderFileItem(child, level + 1))}
</div>
)}
</div>
);
};

return <div>{files.map(file => renderFileItem(file, 0))}</div>;
}

const FileIcon = ({ type }: { type: FileType }) => {
switch (type) {
case 'folder':
return <IconFolder class="w-4 h-4" />;
default:
return <IconFile class="w-4 h-4" />;
}
};
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1cc8ef9

Please sign in to comment.