Skip to content

Commit 9abd5d0

Browse files
authored
feat: new FileUpload component (#512)
1 parent e57dd03 commit 9abd5d0

24 files changed

+1739
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
.fileUpload {
2+
display: flex;
3+
flex-direction: column;
4+
align-items: center;
5+
justify-content: center;
6+
width: 100%;
7+
min-height: 300px;
8+
row-gap: 5px;
9+
}
10+
11+
.fileUpload__label {
12+
color: white;
13+
font-size: 14px;
14+
font-weight: 500;
15+
user-select: none;
16+
}
17+
18+
.fileUpload__dropzone {
19+
padding: 20px;
20+
display: flex;
21+
flex-direction: column;
22+
row-gap: 10px;
23+
align-items: center;
24+
justify-content: center;
25+
border: 1px dashed rgb(42, 42, 40);
26+
width: 100%;
27+
min-height: 200px;
28+
color: #ccc;
29+
border-radius: 6px;
30+
}
31+
32+
.fileUpload__trigger {
33+
background-color: hsl(201 96% 32%);
34+
color: white;
35+
padding: 5px 10px;
36+
border-radius: 4px;
37+
}
38+
39+
.fileUpload__itemGroup {
40+
display: flex;
41+
flex-direction: column;
42+
gap: 3px;
43+
width: 100%;
44+
}
45+
46+
.fileUpload__item {
47+
width: 100%;
48+
display: grid;
49+
padding: 16px;
50+
column-gap: 10px;
51+
border-radius: 6px;
52+
grid-template-columns: auto 1fr auto;
53+
grid-template-areas:
54+
"preview name delete"
55+
"preview size delete";
56+
column-gap: 5px;
57+
border: 1px solid rgb(42, 42, 40);
58+
padding: 10px;
59+
}
60+
61+
.fileUpload__itemPreview {
62+
grid-area: preview;
63+
}
64+
65+
.fileUpload__itemPreviewImage {
66+
width: 50px;
67+
object-fit: scale-down;
68+
height: auto;
69+
aspect-ratio: 1;
70+
}
71+
72+
.fileUpload__itemName {
73+
grid-area: name;
74+
font-size: 14px;
75+
color: #fff;
76+
}
77+
78+
.fileUpload__itemSize {
79+
grid-area: size;
80+
font-size: 14px;
81+
color: rgb(181, 179, 173);
82+
}
83+
84+
.fileUpload__itemDeleteTrigger {
85+
grid-area: delete;
86+
cursor: pointer;
87+
background-color: #a23434;
88+
color: white;
89+
padding: 2px 8px;
90+
border-radius: 4px;
91+
height: max-content;
92+
align-self: center;
93+
}
94+
95+
.formContainer {
96+
width: 100%;
97+
display: flex;
98+
flex-direction: column;
99+
align-items: center;
100+
justify-content: center;
101+
row-gap: 5px;
102+
}
103+
104+
.formContainer > .fileUpload__dropzone {
105+
min-height: 200px;
106+
}
107+
108+
.submit-btn {
109+
background-color: hsl(201 96% 32%);
110+
color: white;
111+
padding: 5px 10px;
112+
border-radius: 4px;
113+
align-self: flex-end;
114+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { FileUpload } from "@kobalte/core/file-upload";
2+
import { For } from "solid-js";
3+
4+
import style from "./file-upload.module.css";
5+
6+
export function BasicExample() {
7+
return (
8+
<FileUpload
9+
class={style.fileUpload}
10+
multiple
11+
maxFiles={5}
12+
onFileAccept={(data) => console.log("data", data)}
13+
onFileReject={(data) => console.log("data", data)}
14+
onFileChange={(data) => console.log("data", data)}
15+
>
16+
<FileUpload.Label class={style.fileUpload__label}>
17+
File Upload
18+
</FileUpload.Label>
19+
<FileUpload.DropZone class={style.fileUpload__dropzone}>
20+
Drop your files here...
21+
<FileUpload.Trigger class={style.fileUpload__trigger}>
22+
Choose files!
23+
</FileUpload.Trigger>
24+
</FileUpload.DropZone>
25+
<FileUpload.HiddenInput />
26+
<FileUpload.ItemGroup class={style.fileUpload__itemGroup}>
27+
<FileUpload.Context>
28+
{(context) => {
29+
return (
30+
<For each={context.acceptedFiles}>
31+
{(file) => (
32+
<FileUpload.Item file={file} class={style.fileUpload__item}>
33+
<FileUpload.ItemPreview
34+
type="image/*"
35+
class={style.fileUpload__itemPreview}
36+
>
37+
<FileUpload.ItemPreviewImage
38+
class={style.fileUpload__itemPreviewImage}
39+
/>
40+
</FileUpload.ItemPreview>
41+
<FileUpload.ItemName class={style.fileUpload__itemName} />
42+
<FileUpload.ItemSize class={style.fileUpload__itemSize} />
43+
<FileUpload.ItemDeleteTrigger
44+
class={style.fileUpload__itemDeleteTrigger}
45+
>
46+
Delete
47+
</FileUpload.ItemDeleteTrigger>
48+
</FileUpload.Item>
49+
)}
50+
</For>
51+
);
52+
}}
53+
</FileUpload.Context>
54+
</FileUpload.ItemGroup>
55+
</FileUpload>
56+
);
57+
}
58+
59+
export function HTMLFormExample() {
60+
let formRef: HTMLFormElement | undefined;
61+
62+
const onSubmit = (event: SubmitEvent) => {
63+
event.preventDefault();
64+
event.stopPropagation();
65+
66+
const formData = new FormData(formRef);
67+
const uploadedFiles = formData.getAll("uploaded-files");
68+
69+
const fileNames = uploadedFiles
70+
.filter((file): file is File => file instanceof File)
71+
.map((file) => file.name);
72+
73+
alert(JSON.stringify(fileNames, null, 2));
74+
};
75+
76+
return (
77+
<form class={style.formContainer} ref={formRef} onSubmit={onSubmit}>
78+
<FileUpload
79+
class={style.fileUpload}
80+
multiple
81+
maxFiles={5}
82+
onFileAccept={(data) => console.log("data", data)}
83+
onFileReject={(data) => console.log("data", data)}
84+
onFileChange={(data) => console.log("data", data)}
85+
>
86+
<FileUpload.Label class={style.fileUpload__label}>
87+
File Upload
88+
</FileUpload.Label>
89+
<FileUpload.DropZone class={style.fileUpload__dropzone}>
90+
Drop your files here...
91+
<FileUpload.Trigger class={style.fileUpload__trigger}>
92+
Choose files!
93+
</FileUpload.Trigger>
94+
</FileUpload.DropZone>
95+
<FileUpload.HiddenInput name="uploaded-files" />
96+
<FileUpload.ItemGroup class={style.fileUpload__itemGroup}>
97+
<FileUpload.Context>
98+
{(context) => {
99+
return (
100+
<For each={context.acceptedFiles}>
101+
{(file) => (
102+
<FileUpload.Item file={file} class={style.fileUpload__item}>
103+
<FileUpload.ItemPreview
104+
type="image/*"
105+
class={style.fileUpload__itemPreview}
106+
>
107+
<FileUpload.ItemPreviewImage
108+
class={style.fileUpload__itemPreviewImage}
109+
/>
110+
</FileUpload.ItemPreview>
111+
<FileUpload.ItemName class={style.fileUpload__itemName} />
112+
<FileUpload.ItemSize class={style.fileUpload__itemSize} />
113+
<FileUpload.ItemDeleteTrigger
114+
class={style.fileUpload__itemDeleteTrigger}
115+
>
116+
Delete
117+
</FileUpload.ItemDeleteTrigger>
118+
</FileUpload.Item>
119+
)}
120+
</For>
121+
);
122+
}}
123+
</FileUpload.Context>
124+
</FileUpload.ItemGroup>
125+
</FileUpload>
126+
<button type="submit" class={style["submit-btn"]}>
127+
Submit Files
128+
</button>
129+
</form>
130+
);
131+
}

apps/docs/src/routes/docs/core.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ const CORE_NAV_SECTIONS: NavSection[] = [
115115
title: "Dropdown Menu",
116116
href: "/docs/core/components/dropdown-menu",
117117
},
118+
{
119+
title: "File Upload",
120+
href: "/docs/core/components/file-upload",
121+
},
118122
{
119123
title: "Hover Card",
120124
href: "/docs/core/components/hover-card",

0 commit comments

Comments
 (0)