-
Notifications
You must be signed in to change notification settings - Fork 763
feat(FileUpload): new component #4102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
vachmara
wants to merge
50
commits into
nuxt:v3
Choose a base branch
from
vachmara:feat-file-upload
base: v3
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
cfc3810
wip
rdjanuar 062a502
wip
rdjanuar 643c1e0
up
rdjanuar 32fdfb9
docs(showcase): fix contrast on light mode
vachmara 07da4b8
Merge branch 'feat/file-upload' of https://github.com/rdjanuar/ui intβ¦
vachmara e284dc8
Merge branch 'nuxt:v3' into v3
vachmara f4ab1be
Merge branch 'v3' of https://github.com/nuxt/ui into v3
vachmara 3542bed
feat: add FileUpload minimal component
vachmara 686843c
fix: use avatar for image preview and icon
vachmara 0a5b982
Merge branch 'v3' of https://github.com/nuxt/ui into feat-file-upload
vachmara 6afc872
Merge branch 'v3' of https://github.com/nuxt/ui into feat-file-upload
vachmara d466b87
Merge branch 'v3' into feat-file-upload
vachmara 9797bb7
Merge branch 'feat-file-upload' of github.com:vachmara/ui into feat-fβ¦
vachmara 5daa768
refactor(FileUpload): cursor-not-allowed when disabled
vachmara c0545fb
feat(FileUpload): emits drag & drop events
vachmara b0c0e2b
test(FileUpload): create tests
vachmara cd42f9a
Merge branch 'v3' into feat-file-upload
vachmara f6d139d
docs(FileUpload): minimal doc
vachmara 83ab576
Merge branch 'v3' into feat-file-upload
vachmara a6ced1e
fix: dragging ui depends also on disabled
vachmara 98ae6cf
fix: avoid firing dragleave event because of nested elements
vachmara d2f1776
test: update FileUpload snapshot
vachmara 2a28a11
test: udpate vue snapshot
vachmara 43a3311
feat: add label and default translations
vachmara a5ffe51
chore(deps): update nuxt framework to ^3.17.4 (v3) (#4197)
renovate[bot] de9e00f
Merge branch 'v3' into feat-file-upload
vachmara a50ca6b
feat: custom file upload item generic type
vachmara 7e5f6fc
test: update file upload type in tests
vachmara ac46e23
refactor: rename file slot to item
vachmara 44603c9
test: item slot with custom type
vachmara 42bd5ae
Merge branch 'v3' into feat-file-upload
vachmara 39be42b
Merge branch 'v3' into feat-file-upload
vachmara 6ca00f7
chore: ky locale
vachmara 1fd6af9
Merge branch 'v3' of github.com:vachmara/ui into feat-file-upload
vachmara 71d00e9
Merge branch 'v3' into feat-file-upload
vachmara bd71479
test: fix
vachmara 9589ce4
Merge branch 'v3' of https://github.com/nuxt/ui into feat-file-upload
vachmara 6577626
feat: improve documentation
vachmara bc1c1e0
docs: add examples
vachmara 07e0845
test: fix vue tests
vachmara 644d64e
Merge branch 'v3' of https://github.com/nuxt/ui into feat-file-upload
vachmara c993d88
chore: vue playground
vachmara 4b809c4
fix: dragleave
vachmara af79ff8
chore: fix typecheck
vachmara ab9840c
fix: onDragLeave
vachmara fd25bbf
tests: update snapshots
vachmara e14641c
up
benjamincanac 637bb13
Merge branch 'v3' of https://github.com/nuxt/ui into feat-file-upload
vachmara fd16686
feat: preview placement
vachmara 8835a49
Merge branch 'feat-file-upload' of github.com:vachmara/ui into feat-fβ¦
vachmara File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
docs/app/components/content/examples/file-upload/FileUploadExtendTypeExample.vue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<script lang="ts" setup> | ||
import type { FileUploadItem } from '@nuxt/ui' | ||
|
||
type UploadWithStatus = FileUploadItem<{ status: 'pending' | 'uploading' | 'done', progress?: number }> | ||
|
||
const value = ref<UploadWithStatus[]>([{ | ||
file: new File([''], 'test.txt'), | ||
status: 'pending' | ||
}]) | ||
</script> | ||
|
||
<template> | ||
<UFileUpload v-model="value" /> | ||
</template> |
86 changes: 86 additions & 0 deletions
86
docs/app/components/content/examples/file-upload/FileUploadFileValidationExample.vue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<script lang="ts" setup> | ||
import type { FileUploadItem } from '@nuxt/ui' | ||
|
||
const value = ref<FileUploadItem[]>([]) | ||
const requirements = [ | ||
{ | ||
test: (file: File) => /^image\//.test(file.type), | ||
text: 'File must be an image.' | ||
}, | ||
{ | ||
test: (file: File) => file.name.length <= 10, | ||
text: 'File name must be less than 10 characters.' | ||
}, | ||
{ | ||
test: (file: File) => !/\s/.test(file.name), | ||
text: 'File name must not contain spaces.' | ||
}, | ||
{ | ||
test: (file: File) => /\.(?:jpg|jpeg|png)$/i.test(file.name), | ||
text: 'File name must end with .jpg, .jpeg, or .png.' | ||
}, | ||
{ | ||
test: (file: File) => file.size < 1024 * 1024, | ||
text: 'File size must be less than 1MB.' | ||
} | ||
] | ||
|
||
const file = computed(() => value.value[0]?.file) | ||
const checks = computed(() => file.value ? requirements.map(req => ({ met: req.test(file.value as File), text: req.text })) : requirements.map(req => ({ met: false, text: req.text }))) | ||
const score = computed(() => checks.value.filter(r => r.met).length) | ||
const color = computed(() => { | ||
if (!file.value) return 'neutral' | ||
if (score.value === 0) return 'neutral' | ||
if (score.value <= 2) return 'error' | ||
if (score.value === 3 || score.value === 4) return 'warning' | ||
return 'success' | ||
}) | ||
const text = computed(() => { | ||
if (!file.value) return 'Select a file' | ||
if (score.value <= 2) return 'File does not meet requirements' | ||
if (score.value === 3 || score.value === 4) return 'Almost valid file' | ||
return 'File is valid!' | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div class="space-y-2"> | ||
<UFormField label="Upload your profile picture" required> | ||
<UFileUpload | ||
v-model="value" | ||
accept="image/*" | ||
upload-icon="i-lucide-upload" | ||
file-icon="i-lucide-file" | ||
required | ||
/> | ||
</UFormField> | ||
|
||
<UProgress | ||
:color="color" | ||
:indicator="text" | ||
:model-value="score" | ||
:max="requirements.length" | ||
size="sm" | ||
/> | ||
|
||
<p class="text-sm font-medium"> | ||
{{ text }}. Must satisfy: | ||
</p> | ||
<ul class="space-y-1" aria-label="File requirements"> | ||
<li | ||
v-for="(req, index) in checks" | ||
:key="index" | ||
class="flex items-center gap-0.5" | ||
:class="req.met ? 'text-success' : 'text-muted'" | ||
> | ||
<UIcon :name="req.met ? 'i-lucide-circle-check' : 'i-lucide-circle-x'" class="size-4 shrink-0" /> | ||
<span class="text-xs font-light"> | ||
{{ req.text }} | ||
<span class="sr-only"> | ||
{{ req.met ? ' - Requirement met' : ' - Requirement not met' }} | ||
</span> | ||
</span> | ||
</li> | ||
</ul> | ||
</div> | ||
</template> |
19 changes: 19 additions & 0 deletions
19
docs/app/components/content/examples/file-upload/FileUploadFormFieldExample.vue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<script lang="ts" setup> | ||
const value = ref([]) | ||
</script> | ||
|
||
<template> | ||
<UFormField | ||
label="Upload your profile picture" | ||
help="We won't share your profile picture." | ||
required | ||
> | ||
<UFileUpload | ||
v-model="value" | ||
accept="image/png" | ||
upload-icon="i-lucide-upload" | ||
file-icon="i-lucide-file" | ||
required | ||
/> | ||
</UFormField> | ||
</template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
--- | ||
title: FileUpload | ||
description: A drag-and-drop file upload component. | ||
category: form | ||
links: | ||
- label: GitHub | ||
icon: i-simple-icons-github | ||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/FileUpload.vue | ||
navigation.badge: New | ||
--- | ||
|
||
## Usage | ||
|
||
Use the `v-model` directive to control the value of the Input. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: [] | ||
--- | ||
:: | ||
|
||
### Accept | ||
|
||
Use the `accept` prop to specify the types of files that can be uploaded. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: [] | ||
accept: 'image/*' | ||
--- | ||
:: | ||
|
||
### Label | ||
|
||
Use the `label` prop to set the label of the component. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: [] | ||
label: 'Upload your image' | ||
--- | ||
:: | ||
|
||
### Upload Icon | ||
|
||
Use the `uploadIcon` prop to set a custom upload icon. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: [] | ||
uploadIcon: 'i-heroicons-cloud-arrow-up-solid' | ||
--- | ||
:: | ||
|
||
::framework-only | ||
#nuxt | ||
:::tip{to="/getting-started/icons/nuxt#theme"} | ||
You can customize this icon globally in your `app.config.ts` under `ui.icons.upload` key. | ||
::: | ||
|
||
#vue | ||
:::tip{to="/getting-started/icons/vue#theme"} | ||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.upload` key. | ||
::: | ||
:: | ||
|
||
### Size | ||
|
||
Use the `size` prop to set the size of the component. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: [] | ||
size: xl | ||
--- | ||
:: | ||
|
||
### Multiple | ||
|
||
Use the `multiple` prop to allow multiple file uploads. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
- multiple | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: [] | ||
multiple: true | ||
--- | ||
:: | ||
|
||
### File Icon | ||
|
||
Use the `fileIcon` prop to set a custom file icon. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
- accept | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: | ||
- file: | ||
name: 'example.txt' | ||
size: 3145728 | ||
type: 'text/plain' | ||
fileIcon: 'i-heroicons-document-text-solid' | ||
accept: 'text/plain' | ||
--- | ||
:: | ||
|
||
::framework-only | ||
#nuxt | ||
:::tip{to="/getting-started/icons/nuxt#theme"} | ||
You can customize this icon globally in your `app.config.ts` under `ui.icons.file` key. | ||
::: | ||
|
||
#vue | ||
:::tip{to="/getting-started/icons/vue#theme"} | ||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.file` key. | ||
::: | ||
:: | ||
|
||
### Disabled | ||
|
||
Use the `disabled` prop to disable the component. | ||
|
||
::component-code | ||
--- | ||
ignore: | ||
- modelValue | ||
external: | ||
- modelValue | ||
externalTypes: | ||
- FileUploadItem[] | ||
props: | ||
modelValue: [] | ||
disabled: true | ||
--- | ||
:: | ||
|
||
## Examples | ||
|
||
### With custom type | ||
|
||
You can extend the type to include additional properties. | ||
|
||
::component-example | ||
--- | ||
name: 'file-upload-extend-type-example' | ||
--- | ||
:: | ||
|
||
### Within a FormField | ||
|
||
You can use the FileUpload within a [FormField](/components/form-field) component to display a label, help text, required indicator, etc. | ||
|
||
::component-example | ||
--- | ||
name: 'file-upload-form-field-example' | ||
--- | ||
:: | ||
|
||
::tip{to="/components/form"} | ||
It also provides validation and error handling when used within a **Form** component. | ||
:: | ||
|
||
### With file validation | ||
|
||
You can build a custom validation function to check the file type and size. | ||
|
||
::component-example | ||
--- | ||
collapse: true | ||
name: 'file-upload-file-validation-example' | ||
--- | ||
:: | ||
|
||
|
||
## API | ||
|
||
### Props | ||
|
||
:component-props | ||
|
||
### Slots | ||
|
||
:component-slots | ||
|
||
### Emits | ||
|
||
:component-emits | ||
|
||
### Expose | ||
|
||
When accessing the component via a template ref, you can use the following: | ||
|
||
| Name | Type | | ||
| ---- | ---- | | ||
| `fileInputRef`{lang="ts-type"} | `Ref<HTMLInputElement \| null>`{lang="ts-type"} | | ||
|
||
## Theme | ||
|
||
:component-theme |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.