File upload
אזור שחרור קבצים ורשימת קבצים.
- דפדפנים
- Chrome 123+Firefox 121+Safari 17.5+
- שכבה
- 1
- גודל חבילה דחוסה
- 2.27 kB
- מגבלת פריסה כפויה
- 0 ms
- מגבלת Axe
- 0
- תואם SSR
- כן
- חוזה מפורסם
- סביבת בדיקות
מגרש ניסויים
"use client";
import { FileUpload } from "@gunkin/uiify";
export default function Example() {
return (
<FileUpload.Root multiple maxFiles={3} maxSize={5000000} className="">
<FileUpload.HiddenInput name="attachments" aria-label="Attachments" />
<FileUpload.Trigger as="button" className="">
Choose files
</FileUpload.Trigger>
<FileUpload.Dropzone className="">Drop files here</FileUpload.Dropzone>
<FileUpload.List className="">
{(files) =>
files.map((file, index) => (
<FileUpload.Item key={file.name} index={index} className="">
<span className="">
<strong>{file.name}</strong>
<small className="">{(file.size / 1024).toFixed(1)} KB</small>
</span>
<FileUpload.ItemRemove index={index} className="" aria-label={"Remove " + file.name}>
×
</FileUpload.ItemRemove>
</FileUpload.Item>
))
}
</FileUpload.List>
</FileUpload.Root>
);
}/* File upload — trigger button with dashed dropzone. */
@layer components {
[data-uiify-file-upload] {
display: grid;
gap: var(--space-md);
inline-size: 100%;
max-inline-size: 24rem;
}
[data-uiify-file-upload] [data-part="dropzone"] {
display: flex;
align-items: center;
justify-content: center;
min-block-size: 8rem;
padding: var(--space-lg);
border: 2px dashed var(--input);
border-radius: var(--radius-xl);
background-color: var(--secondary);
color: var(--muted-foreground);
font-size: var(--text-sm);
text-align: center;
transition: var(--transition-colors);
}
[data-uiify-file-upload] [data-part="dropzone"]:hover,
[data-uiify-file-upload] [data-part="dropzone"][data-dragging] {
border-color: var(--primary);
background-color: color-mix(in oklch, var(--primary) 8%, transparent);
color: var(--foreground);
}
[data-uiify-file-upload] [data-part="dropzone"][data-disabled] {
opacity: 0.5;
cursor: not-allowed;
}
[data-uiify-file-upload] [data-part="list"] {
display: grid;
gap: var(--space-xs);
margin: 0;
padding: 0;
list-style: none;
}
[data-uiify-file-upload] [data-part="item"] {
display: flex;
align-items: center;
gap: var(--space-sm);
padding: var(--space-sm) var(--space-md);
border: var(--border-width) solid var(--border);
border-radius: var(--radius-lg);
background-color: var(--card);
font-size: var(--text-sm);
}
}
@layer components {
[data-uiify-file-upload] [data-part="file-info"] {
display: grid;
gap: 0.25rem;
min-inline-size: 0;
flex: 1;
overflow-wrap: anywhere;
}
[data-uiify-file-upload] small {
color: var(--muted-foreground);
}
[data-uiify-file-upload] [data-part="remove"] {
display: grid;
place-items: center;
flex: none;
inline-size: 2rem;
block-size: 2rem;
margin-inline-start: auto;
border: 0;
border-radius: 50%;
background: var(--secondary);
color: var(--foreground);
font-size: 1.25rem;
line-height: 1;
cursor: pointer;
}
[data-uiify-file-upload] [data-part="remove"]:hover {
background: var(--accent);
}
[data-uiify-file-upload] [data-part="remove"]:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
}"use client";
import { FileUpload } from "@gunkin/uiify";
export default function Example() {
return (
<FileUpload.Root
multiple
maxFiles={3}
maxSize={5000000}
className="grid gap-md [inline-size:100%] [max-inline-size:24rem]"
>
<FileUpload.HiddenInput name="attachments" aria-label="Attachments" />
<FileUpload.Trigger
as="button"
className={
"relative isolate inline-flex items-center justify-center gap-1.5 min-h-10 min-w-10 px-5 py-px border-[len" +
"gth:var(--border-width)] border-solid border-transparent rounded-full bg-clip-padding text-base font-medi" +
"um leading-6 no-underline cursor-pointer [transition:var(--transition-colors)] focus-visible:[outline:non" +
"e] focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_20%,_transparent)] d" +
"isabled:opacity-50 disabled:cursor-not-allowed data-[disabled]:opacity-50 data-[disabled]:cursor-not-allo" +
"wed [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:[--uiify-button-rim:rgb(255_255_255_/" +
"_20%)] [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:[--uiify-button-wash:rgb(255_255_2" +
"55_/_15%)] [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:border-transparent [&:is([data" +
"-selected],[aria-pressed=true],[aria-current=page])]:bg-primary [&:is([data-selected],[aria-pressed=true]" +
",[aria-current=page])]:text-primary-foreground [:checked+&]:[--uiify-button-rim:rgb(255_255_255_/_20%)] [" +
":checked+&]:[--uiify-button-wash:rgb(255_255_255_/_15%)] [:checked+&]:border-transparent [:checked+&]:bg-" +
"primary [:checked+&]:text-primary-foreground [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:[&:is([d" +
"ata-selected],[aria-pressed=true],[aria-current=page]):hover:not(:disabled,[data-disabled])]:[background-" +
"color:color-mix(in_oklch,_var(--primary)_88%,_var(--primary-foreground))] [@media_(any-hover:_hover)_and_" +
"(any-pointer:_fine)]:[:checked:not(:disabled)+&:hover]:[background-color:color-mix(in_oklch,_var(--primar" +
"y)_88%,_var(--primary-foreground))] bg-foreground text-background [@media_(any-hover:_hover)_and_(any-poi" +
"nter:_fine)]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foregro" +
"und)_90%,_var(--background))] active:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_ok" +
"lch,_var(--foreground)_80%,_var(--background))]"
}
>
Choose files
</FileUpload.Trigger>
<FileUpload.Dropzone
className="
flex items-center justify-center [min-block-size:8rem] p-lg [border-width:2px] [border-style:dashed]
border-input rounded-xl bg-secondary text-muted-foreground text-sm text-center
[transition:var(--transition-colors)] hover:border-primary
hover:[background-color:color-mix(in_oklch,_var(--primary)_8%,_transparent)] hover:text-foreground
data-[dragging]:border-primary
data-[dragging]:[background-color:color-mix(in_oklch,_var(--primary)_8%,_transparent)]
data-[dragging]:text-foreground data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed
"
>
Drop files here
</FileUpload.Dropzone>
<FileUpload.List className="grid gap-xs m-0 p-0 list-none">
{(files) =>
files.map((file, index) => (
<FileUpload.Item
key={file.name}
index={index}
className="
flex items-center gap-sm [padding:var(--space-sm)_var(--space-md)] border-[length:var(--border-width)]
border-solid border-border rounded-lg bg-card text-sm
"
>
<span className="grid [gap:0.25rem] [min-inline-size:0] [flex:1] [overflow-wrap:anywhere]">
<strong>{file.name}</strong>
<small className="text-muted-foreground">{(file.size / 1024).toFixed(1)} KB</small>
</span>
<FileUpload.ItemRemove
index={index}
className="
grid place-items-center flex-none [inline-size:2rem] [block-size:2rem] [margin-inline-start:auto]
border-0 border-current border-none rounded-[50%] bg-secondary text-foreground [font-size:1.25rem]
leading-none cursor-pointer hover:bg-accent focus-visible:[outline:2px_solid_var(--ring)]
focus-visible:[outline-offset:2px]
"
aria-label={"Remove " + file.name}
>
×
</FileUpload.ItemRemove>
</FileUpload.Item>
))
}
</FileUpload.List>
</FileUpload.Root>
);
}API
FileUpload.Root
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
children | ReactNode | — | חלקי העלאת קבצים: קלט מוסתר, כפתור פתיחה, אזור שחרור ורשימה. |
value | readonly File[] | — | הקבצים הנוכחיים, ההופכים את הרכיב למבוקר. |
defaultValue | readonly File[] | [] | הקבצים ההתחלתיים של הרכיב הלא מבוקר. |
onValueChange | (files: File[]) => void | — | נקראת עם הקבצים שהתקבלו בכל שינוי בבחירה. |
onReject | (rejections: FileRejection[]) => void | — | נקראת עם הקבצים שנכשלו באימות והסיבות לכך. |
accept | string[] | [] | סוגי הקבצים המותרים, כסיומות או כסוגי MIME. |
maxFiles | number | — | המספר המרבי של קבצים שניתן להחזיק בו-זמנית. |
maxSize | number | — | גודל הקובץ המרבי המותר בבתים. |
minSize | number | — | גודל הקובץ המזערי המותר בבתים. |
multiple | boolean | false | מאפשר לבורר לבחור יותר מקובץ אחד. |
disabled | boolean | false | משבית את הבורר, אזור השחרור וכפתורי ההסרה. |
FileUpload.HiddenInput
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
className | string | — | המחלקה המוחלת על קלט הקובץ המובנה. |
FileUpload.Trigger
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
children | ReactNode | — | תוכן הפקד שפותח את בורר הקבצים. |
className | string | — | המחלקה המוחלת על אלמנט הפתיחה. |
render | ReactElement | ((props, state) => ReactElement) | — | החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות. |
FileUpload.Dropzone
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
children | ReactNode | — | תוכן יעד השחרור. |
className | string | — | המחלקה המוחלת על אלמנט אזור השחרור. |
render | ReactElement | ((props, state) => ReactElement) | — | החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות. |
FileUpload.List
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
children | ReactNode | ((files: readonly File[]) => ReactNode) | — | מציג קבצים שהתקבלו מתוך הרשימה החיה. שינויים בערך המבוקר ואיפוסים חיצוניים משתקפים מיד. |
className | string | — | המחלקה המוחלת על אלמנט הרשימה. |
FileUpload.Item
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
children | ReactNode | — | תוכן המתאר את הקובץ, כגון שמו וגודלו. |
className | string | — | המחלקה המוחלת על אלמנט הפריט. |
index* | number | — | מיקום הקובץ שפריט זה מייצג. |
render | ReactElement | ((props, state) => ReactElement) | — | החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות. |
FileUpload.ItemRemove
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
className | string | — | המחלקה המוחלת על כפתור ההסרה. |
index* | number | — | מיקום הקובץ שכפתור זה מסיר. |
FileRejection
| מאפיין | טיפוס | ברירת מחדל | תיאור |
|---|---|---|---|
file* | File | — | הקובץ שלא התקבל. |
reason* | "accept" | "max-size" | "max-files" | — | הכלל שהקובץ לא עמד בו. |
מקלדת
| מקש | פעולה |
|---|---|
| Enter / Space | פתיחת בורר הקבצים מתוך Trigger או הסרת קובץ ממוקד |
| Tab | מעבר בין מפעיל הבורר לבין פקדי הסרת הקבצים |
נגישות
תנו ל-HiddenInput תווית נגישה. List מקבל את הקבצים שהתקבלו ומרנדר את שמותיהם, גודלם וכפתורי הסרה עם תוויות. Root תומך ב-value מבוקר, ב-maxFiles מצטבר ובאימות maxSize ו-accept; onReject מדווח על קבצים שנדחו. שורשים מושבתים חוסמים פעולות בחירה, שחרור והסרה. הטיפול בקבצים דורש הידרציה.
נקודות חיבור לעיצוב
data-uiify-file-uploaddata-partdata-disableddata-state