Skip to content

Commit 8220be0

Browse files
committed
add widget system
1 parent 93fb5b3 commit 8220be0

22 files changed

Lines changed: 286 additions & 145 deletions

File tree

src/app.tsx

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,13 @@
1-
import { AppWindowIcon, CodeIcon } from "lucide-react";
2-
import { useTranslation } from "react-i18next";
3-
import { FileExplorer } from "@/components/features/file-explorer";
1+
import { Content } from "@/components/features/content";
42
import { Hero } from "@/components/global/hero";
5-
import { Main } from "@/components/global/main";
63
import { NavBar } from "@/components/global/navbar";
7-
import { Card, CardContent } from "@/components/ui/card";
8-
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
4+
import { Settings } from "@/components/global/settings";
95
import { LangProvider } from "@/contexts/lang";
10-
import { IndexProvider } from "@/contexts/nginx-index";
6+
import { IndexProvider } from "@/contexts/nginx";
117
import { SearchProvider } from "@/contexts/search";
128
import { SettingsProvider } from "@/contexts/settings";
139
import { ThemeProvider } from "@/contexts/theme";
1410
import { parseTemplate } from "@/lib/client/nginx";
15-
import { Settings } from "./components/global/settings";
16-
17-
const AppTabs: React.FC<{ path: string }> = ({ path }) => {
18-
const { t } = useTranslation();
19-
20-
return (
21-
<Main path={path}>
22-
<Tabs defaultValue="preview">
23-
<TabsList>
24-
<TabsTrigger value="explorer">
25-
<AppWindowIcon />
26-
{t("tabs.fileExplorer")}
27-
</TabsTrigger>
28-
<TabsTrigger value="binviewer">
29-
<CodeIcon />
30-
{t("tabs.binViewer")}
31-
</TabsTrigger>
32-
</TabsList>
33-
<TabsContent value="explorer">
34-
<Card className="py-2">
35-
<CardContent className="px-2">
36-
<FileExplorer />
37-
</CardContent>
38-
</Card>
39-
</TabsContent>
40-
<TabsContent value="binviewer">
41-
<div className="p-4">
42-
<h2 className="text-xl font-bold">{t("tabs.binViewer")}</h2>
43-
<p className="text-muted-foreground">
44-
{t("binViewer.description")}
45-
</p>
46-
</div>
47-
</TabsContent>
48-
</Tabs>
49-
</Main>
50-
);
51-
};
5211

5312
const App: React.FC = () => {
5413
const { path, files } = parseTemplate();
@@ -62,7 +21,7 @@ const App: React.FC = () => {
6221
<Settings />
6322
<NavBar />
6423
<Hero />
65-
<AppTabs path={path} />
24+
<Content />
6625
</SearchProvider>
6726
</ThemeProvider>
6827
</SettingsProvider>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useTranslation } from "react-i18next";
2+
3+
export const BinViewer: React.FC = () => {
4+
const { t } = useTranslation();
5+
6+
return (
7+
<div className="p-4">
8+
<h2 className="text-xl font-bold">{t("tabs.binViewer")}</h2>
9+
<p className="text-muted-foreground">{t("binViewer.description")}</p>
10+
</div>
11+
);
12+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { BinViewer } from "./components/bin-viewer";
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { AppWindowIcon, CodeIcon } from "lucide-react";
2+
import { useTranslation } from "react-i18next";
3+
import { BinViewer } from "@/components/features/bin-viewer";
4+
import { FileExplorer } from "@/components/features/file-explorer";
5+
import { Main } from "@/components/global/main";
6+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
7+
import { useIndex } from "@/hooks/nginx";
8+
9+
export const Content: React.FC = () => {
10+
const { path } = useIndex();
11+
const { t } = useTranslation();
12+
13+
return (
14+
<Main path={path}>
15+
<Tabs defaultValue="preview">
16+
<TabsList>
17+
<TabsTrigger value="explorer">
18+
<AppWindowIcon />
19+
{t("tabs.fileExplorer")}
20+
</TabsTrigger>
21+
<TabsTrigger value="binviewer">
22+
<CodeIcon />
23+
{t("tabs.binViewer")}
24+
</TabsTrigger>
25+
</TabsList>
26+
<TabsContent value="explorer">
27+
<FileExplorer />
28+
</TabsContent>
29+
<TabsContent value="binviewer">
30+
<BinViewer />
31+
</TabsContent>
32+
</Tabs>
33+
</Main>
34+
);
35+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { Content } from "./components/content";

src/components/features/file-explorer/components/file-explorer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect, useState } from "react";
2+
import { Widgets } from "@/components/features/widgets";
23
import { useSearch } from "@/hooks/search";
34
import { FileTable } from "./file-table";
45
import { SearchResultTable } from "./search-result-table";
@@ -14,6 +15,7 @@ export const FileExplorer: React.FC = () => {
1415
return (
1516
<>
1617
<div className={active ? "hidden" : ""}>
18+
<Widgets />
1719
<FileTable />
1820
</div>
1921
<div className={active ? "" : "hidden"}>

src/components/features/file-explorer/components/file-table.tsx

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import {
1111
TableHeader,
1212
TableRow,
1313
} from "@/components/ui/table";
14-
import { useIndex } from "@/hooks/nginx-index";
14+
import { useIndex } from "@/hooks/nginx";
1515
import { getFileIcon, sortFiles } from "@/lib/client/utils";
1616
import type { SortColumn, SortDirection } from "@/types/files";
17+
import { Wrapper } from "./wrapper";
1718

1819
export const FileTable: React.FC = () => {
1920
const { t } = useTranslation();
@@ -59,74 +60,76 @@ export const FileTable: React.FC = () => {
5960
};
6061

6162
return (
62-
<div className="rounded-md overflow-hidden">
63-
<Table>
64-
<TableHeader>
65-
<TableRow>
66-
<TableHead className="w-full">
67-
<Button
68-
variant="ghost"
69-
size="sm"
70-
onClick={() => toggleSort("name")}
71-
>
72-
{t("fileExplorer.name")}
73-
<SortIcon column="name" />
74-
</Button>
75-
</TableHead>
63+
<Wrapper>
64+
<div className="rounded-md overflow-hidden">
65+
<Table>
66+
<TableHeader>
67+
<TableRow>
68+
<TableHead className="w-full">
69+
<Button
70+
variant="ghost"
71+
size="sm"
72+
onClick={() => toggleSort("name")}
73+
>
74+
{t("fileExplorer.name")}
75+
<SortIcon column="name" />
76+
</Button>
77+
</TableHead>
7678

77-
<TableHead className="text-right whitespace-nowrap w-min">
78-
<Button
79-
variant="ghost"
80-
size="sm"
81-
onClick={() => toggleSort("size")}
82-
>
83-
{t("fileExplorer.size")}
84-
<SortIcon column="size" />
85-
</Button>
86-
</TableHead>
79+
<TableHead className="text-right whitespace-nowrap w-min">
80+
<Button
81+
variant="ghost"
82+
size="sm"
83+
onClick={() => toggleSort("size")}
84+
>
85+
{t("fileExplorer.size")}
86+
<SortIcon column="size" />
87+
</Button>
88+
</TableHead>
8789

88-
<TableHead className="text-right whitespace-nowrap w-min">
89-
<Button
90-
variant="ghost"
91-
size="sm"
92-
onClick={() => toggleSort("date")}
93-
>
94-
{t("fileExplorer.modified")}
95-
<SortIcon column="date" />
96-
</Button>
97-
</TableHead>
98-
</TableRow>
99-
</TableHeader>
100-
<TableBody>
101-
{sortedFiles.map((file) => {
102-
const Icon = getFileIcon(file.name, file.isDirectory);
103-
return (
104-
<TableRow key={file.link}>
105-
<TableCell className="font-medium">
106-
<a href={file.link} className="flex items-center gap-2">
107-
<Icon className="h-4 w-4" />
108-
{file.name.replace(/\/+$/, "")}
109-
</a>
110-
</TableCell>
90+
<TableHead className="text-right whitespace-nowrap w-min">
91+
<Button
92+
variant="ghost"
93+
size="sm"
94+
onClick={() => toggleSort("date")}
95+
>
96+
{t("fileExplorer.modified")}
97+
<SortIcon column="date" />
98+
</Button>
99+
</TableHead>
100+
</TableRow>
101+
</TableHeader>
102+
<TableBody>
103+
{sortedFiles.map((file) => {
104+
const Icon = getFileIcon(file.name, file.isDirectory);
105+
return (
106+
<TableRow key={file.link}>
107+
<TableCell className="font-medium">
108+
<a href={file.link} className="flex items-center gap-2">
109+
<Icon className="h-4 w-4" />
110+
{file.name.replace(/\/+$/, "")}
111+
</a>
112+
</TableCell>
111113

112-
<TableCell className="text-right">
113-
{!file.isDirectory && file.size ? (
114-
<Badge variant="secondary" className="inline-flex">
115-
{file.size.raw}
116-
</Badge>
117-
) : (
118-
"-"
119-
)}
120-
</TableCell>
114+
<TableCell className="text-right">
115+
{!file.isDirectory && file.size ? (
116+
<Badge variant="secondary" className="inline-flex">
117+
{file.size.raw}
118+
</Badge>
119+
) : (
120+
"-"
121+
)}
122+
</TableCell>
121123

122-
<TableCell className="text-right whitespace-nowrap font-mono pr-5 text-xs">
123-
{formatDate(file.date)}
124-
</TableCell>
125-
</TableRow>
126-
);
127-
})}
128-
</TableBody>
129-
</Table>
130-
</div>
124+
<TableCell className="text-right whitespace-nowrap font-mono pr-5 text-xs">
125+
{formatDate(file.date)}
126+
</TableCell>
127+
</TableRow>
128+
);
129+
})}
130+
</TableBody>
131+
</Table>
132+
</div>
133+
</Wrapper>
131134
);
132135
};

src/components/features/file-explorer/components/search-result-table.tsx

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
} from "@/components/ui/table";
1919
import { useFileExplorer } from "@/hooks/file-explorer";
2020
import { useSearch } from "@/hooks/search";
21+
import { Wrapper } from "./wrapper";
2122

2223
export const SearchResultTable: React.FC = () => {
2324
const { t } = useTranslation();
@@ -38,50 +39,58 @@ export const SearchResultTable: React.FC = () => {
3839

3940
if (error) {
4041
return (
41-
<div>
42-
{t("errors.prefix")} {error.message}
43-
</div>
42+
<Wrapper>
43+
<div>
44+
{t("errors.prefix")} {error.message}
45+
</div>
46+
</Wrapper>
4447
);
4548
}
4649

4750
if (loading) {
4851
return (
49-
<Empty className="w-full">
50-
<EmptyHeader>
51-
<EmptyMedia variant="icon">
52-
<Spinner />
53-
</EmptyMedia>
54-
<EmptyTitle>{t("fileExplorer.processing")}</EmptyTitle>
55-
<EmptyDescription>{t("fileExplorer.searching")}</EmptyDescription>
56-
</EmptyHeader>
57-
</Empty>
52+
<Wrapper>
53+
<Empty className="w-full">
54+
<EmptyHeader>
55+
<EmptyMedia variant="icon">
56+
<Spinner />
57+
</EmptyMedia>
58+
<EmptyTitle>{t("fileExplorer.processing")}</EmptyTitle>
59+
<EmptyDescription>{t("fileExplorer.searching")}</EmptyDescription>
60+
</EmptyHeader>
61+
</Empty>
62+
</Wrapper>
5863
);
5964
}
6065

6166
if (results.length === 0) {
6267
return (
63-
<div className="text-center py-8 text-muted-foreground">
64-
{t("fileExplorer.noResults")}
65-
</div>
68+
<Wrapper>
69+
<div className="text-center py-8 text-muted-foreground">
70+
{t("fileExplorer.noResults")}
71+
</div>
72+
</Wrapper>
6673
);
6774
}
6875

6976
return (
70-
<Table>
71-
<TableHeader>
72-
<TableRow>
73-
<TableHead>{t("fileExplorer.name")}</TableHead>
74-
</TableRow>
75-
</TableHeader>
76-
<TableBody>
77-
{results.slice(0, 10000).map((result) => (
78-
<TableRow key={result.filename}>
79-
<TableCell>
80-
<a href={result.href}>{result.filename}</a>
81-
</TableCell>
77+
<Wrapper>
78+
<Table>
79+
<TableHeader>
80+
<TableRow>
81+
<TableHead>{t("fileExplorer.name")}</TableHead>
8282
</TableRow>
83-
))}
84-
</TableBody>
85-
</Table>
83+
</TableHeader>
84+
<TableBody>
85+
{results.slice(0, 10000).map((result) => (
86+
<TableRow key={result.filename}>
87+
<TableCell>
88+
<a href={result.href}>{result.filename}</a>
89+
</TableCell>
90+
</TableRow>
91+
))}
92+
</TableBody>
93+
</Table>
94+
</Wrapper>
8695
);
8796
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Card, CardContent } from "@/components/ui/card";
2+
3+
export const Wrapper: React.FC<{ children: React.ReactNode }> = ({
4+
children,
5+
}) => (
6+
<Card className="py-2">
7+
<CardContent className="px-2">{children}</CardContent>
8+
</Card>
9+
);

0 commit comments

Comments
 (0)