Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/file-upload-improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@geajs/ui": patch
---

Fixed and enhanced FileUpload:
- use typed `Props` to enable autocompletion
- show drag-active overlay state
- file list shows file size (with human-readable formatting) and per-file delete button
- `preventDocumentDrop` disables dropping files outside dropzone
- i18n
32 changes: 25 additions & 7 deletions .cursor/skills/gea-ui-components/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,32 @@ Drag-and-drop file upload area.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string` | — | Label text |
| `accept` | `string \| Record<string, string[]>` | — | Accepted file types |
| `label` | `JSXNode` | — | Label text |
| `accept` | `string \| string[] \| Record<string, string[]>` | — | Accepted file types |
| `maxFiles` | `number` | — | Maximum number of files |
| `multiple` | `boolean` | — | Allow multiple files |
| `onFileChange` | `(details) => void` | — | Files changed |

```tsx
<FileUpload label="Upload images" accept="image/*" maxFiles={3} multiple />
| `minFileSize` | `number` | — | Minimum file size in bytes |
| `maxFileSize` | `number` | — | Maximum file size in bytes |
| `allowDrop` | `boolean` | `true` | Allow drag&drop selection |
| `preventDocumentDrop` | `boolean` | `true` | Prevent dropping file outside the dropzone |
| `name` | `string` | — | Input name (for usage in form) |
| `class` | `string` | — | Class name(s) added to the root element |
| `disabled` | `boolean` | `false` | Disable interaction |
| `formatFileSize` | `(bytes: number) => string` | localized, 2 decimal points, suffix | Function formatting file size in the file list |
| `onFileChange` | `(details: { acceptedFiles: File[], rejectedFiles: { file: File, errors: string[] }[] }) => void` | — | Called when files are selected |
| `translations` | `Partial<FileUploadTranslations>` | see Translations | Override UI strings |

#### Translations

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `dropzone` | `string` | Drag and drop files here | Default dropzone text |
| `dropzoneButton` | `string` | Choose Files | Button triggering file selection |
| `dropzoneActive` | `string` | Drop your files here | Dropzone text while files are being dragged |
| `deleteFile` | `(file: File) => string` | ``(file: File) => `Remove file ${file.name}` `` | Button removing single file from the list |
| `clearAllButton` | `string` | Clear all | Button removing all files from the list |

```tsx
<FileUpload label="Upload images" accept="image/*" maxFiles={3} />
```

### HoverCard
Expand Down
24 changes: 21 additions & 3 deletions docs/gea-ui/interactive-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,11 +578,29 @@ import { FileUpload } from '@geajs/ui'

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` | — | Label text |
| `accept` | `Record<string, string[]>` | — | Accepted MIME types and extensions |
| `label` | `JSXNode` | — | Label text |
| `accept` | `string \| string[] \| Record<string, string[]>` | — | Accepted MIME types and extensions |
| `maxFiles` | `number` | — | Maximum number of files |
| `minFileSize` | `number` | — | Minimum file size in bytes |
| `maxFileSize` | `number` | — | Maximum file size in bytes |
| `onFileChange` | `(details: { acceptedFiles: File[], rejectedFiles: File[] }) => void` | — | Called when files are selected |
| `allowDrop` | `boolean` | `true` | Allow drag&drop selection |
| `preventDocumentDrop` | `boolean` | `true` | Prevent dropping file outside the dropzone |
| `name` | `string` | — | Input name (for usage in form) |
| `class` | `string` | — | Class name(s) added to the root element |
| `disabled` | `boolean` | `false` | Disable interaction |
| `formatFileSize` | `(bytes: number) => string` | localized, 2 decimal points, suffix | Function formatting file size in the file list |
| `onFileChange` | `(details: { acceptedFiles: File[], rejectedFiles: { file: File, errors: string[] }[] }) => void` | — | Called when files are selected |
| `translations` | `Partial<FileUploadTranslations>` | see Translations | Override UI strings |

### Translations

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `dropzone` | `string` | Drag and drop files here | Default dropzone text |
| `dropzoneButton` | `string` | Choose Files | Button triggering file selection |
| `dropzoneActive` | `string` | Drop your files here | Dropzone text while files are being dragged |
| `deleteFile` | `(file: File) => string` | ``(file: File) => `Remove file ${file.name}` `` | Button removing single file from the list |
| `clearAllButton` | `string` | Clear all | Button removing all files from the list |

## Toast

Expand Down
143 changes: 136 additions & 7 deletions examples/docs/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1791,9 +1791,9 @@ export default class App extends Component {
<p class="doc-desc">Drag-and-drop or click-to-browse file uploader.</p>
<div class="demo-block">
<div class="demo-preview" style={{ width: '100%' }}>
<FileUpload maxFiles={3} multiple />
<FileUpload maxFiles={3} />
</div>
<div class="demo-code">{`<FileUpload maxFiles={3} multiple />`}</div>
<div class="demo-code">{`<FileUpload maxFiles={3} />`}</div>
</div>
<h3>API</h3>
<table class="prop-table">
Expand All @@ -1809,7 +1809,7 @@ export default class App extends Component {
<tr>
<td class="prop-name">accept</td>
<td class="prop-type">
<code>Record&lt;string, string[]&gt;</code>
<code>string | string[] | Record&lt;string, string[]&gt;</code>
</td>
<td class="prop-default">—</td>
<td>Accepted file types</td>
Expand All @@ -1822,6 +1822,14 @@ export default class App extends Component {
<td class="prop-default">—</td>
<td>Maximum file count</td>
</tr>
<tr>
<td class="prop-name">minFileSize</td>
<td class="prop-type">
<code>number</code>
</td>
<td class="prop-default">—</td>
<td>Min file size in bytes</td>
</tr>
<tr>
<td class="prop-name">maxFileSize</td>
<td class="prop-type">
Expand All @@ -1831,20 +1839,141 @@ export default class App extends Component {
<td>Max file size in bytes</td>
</tr>
<tr>
<td class="prop-name">multiple</td>
<td class="prop-name">allowDrop</td>
<td class="prop-type">
<code>boolean</code>
</td>
<td class="prop-default">true</td>
<td>Allow drag&drop selection</td>
</tr>
<tr>
<td class="prop-name">preventDocumentDrop</td>
<td class="prop-type">
<code>boolean</code>
</td>
<td class="prop-default">true</td>
<td>Prevent dropping file outside the dropzone</td>
</tr>
<tr>
<td class="prop-name">name</td>
<td class="prop-type">
<code>string</code>
</td>
<td class="prop-default">—</td>
<td>Input name (for usage in form)</td>
</tr>
<tr>
<td class="prop-name">class</td>
<td class="prop-type">
<code>string</code>
</td>
<td class="prop-default">—</td>
<td>Class name(s) added to the root element</td>
</tr>
<tr>
<td class="prop-name">label</td>
<td class="prop-type">
<code>JSXNode</code>
</td>
<td class="prop-default">—</td>
<td>Label above the dropzone</td>
</tr>
<tr>
<td class="prop-name">disabled</td>
<td class="prop-type">
<code>boolean</code>
</td>
<td class="prop-default">false</td>
<td>Allow multiple files</td>
<td>Disable interaction</td>
</tr>
<tr>
<td class="prop-name">formatFileSize</td>
<td class="prop-type">
<code>{'(bytes: number) => string'}</code>
</td>
<td class="prop-default">localized, 2 decimal points, suffix</td>
<td>Function formatting file size in the file list</td>
</tr>
<tr>
<td class="prop-name">onFileChange</td>
<td class="prop-type">
<code>{'(details) => void'}</code>
<code>
{
'(details: { acceptedFiles: File[], rejectedFiles: { file: File, errors: string[] }[] }) => void'
}
</code>
</td>
<td class="prop-default">—</td>
<td>File change callback</td>
<td>
<a href="https://zagjs.com/components/react/file-upload#listening-to-file-changes">
File change callback
</a>
</td>
</tr>
<tr>
<td class="prop-name">translations</td>
<td class="prop-type">
<code>
Partial{'<'}FileUploadTranslations{'>'}
</code>
</td>
<td class="prop-default">see Translations</td>
<td>Override UI strings</td>
</tr>
</tbody>
</table>
<h3>Translations</h3>
<table class="prop-table">
<thead>
<tr>
<th>Key</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="prop-name">dropzone</td>
<td class="prop-type">
<code>string</code>
</td>
<td class="prop-default">Drag and drop files here</td>
<td>Default dropzone text</td>
</tr>
<tr>
<td class="prop-name">dropzoneButton</td>
<td class="prop-type">
<code>string</code>
</td>
<td class="prop-default">Choose Files</td>
<td>Button triggering file selection</td>
</tr>
<tr>
<td class="prop-name">dropzoneActive</td>
<td class="prop-type">
<code>string</code>
</td>
<td class="prop-default">Drop your files here</td>
<td>Dropzone text while files are being dragged</td>
</tr>
<tr>
<td class="prop-name">deleteFile</td>
<td class="prop-type">
<code>{'(file: File) => string'}</code>
</td>
<td class="prop-default">
<code>{`(file: File) => 'Remove file ' + file.name`}</code>
</td>
<td>Button removing single file from the list</td>
</tr>
<tr>
<td class="prop-name">clearAllButton</td>
<td class="prop-type">
<code>string</code>
</td>
<td class="prop-default">Clear all</td>
<td>Button removing all files from the list</td>
</tr>
</tbody>
</table>
Expand Down
2 changes: 1 addition & 1 deletion examples/forms/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export default class App extends Component {
<CardDescription>Upload identity documents for verification.</CardDescription>
</CardHeader>
<CardContent>
<FileUpload label="Upload Files" maxFiles={3} multiple />
<FileUpload label="Upload Files" maxFiles={3} />
</CardContent>
</Card>

Expand Down
2 changes: 1 addition & 1 deletion examples/showcase/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ export default class App extends Component {
<p>Drag-and-drop or click to upload files.</p>
</div>
<div class="component-card-body">
<FileUpload maxFiles={3} multiple />
<FileUpload maxFiles={3} />
</div>
</div>

Expand Down
Loading
Loading