-
- Playground
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts
index 94ffb6da..a1c6890b 100644
--- a/playground/nuxt.config.ts
+++ b/playground/nuxt.config.ts
@@ -16,6 +16,10 @@ export default defineNuxtConfig({
compatibilityDate: '2024-07-09',
+ hub: {
+ blob: true
+ },
+
vite: {
optimizeDeps: {
// prevents reloading page when navigating between components
diff --git a/playground/server/api/blob.put.ts b/playground/server/api/blob.put.ts
new file mode 100644
index 00000000..cf1a2500
--- /dev/null
+++ b/playground/server/api/blob.put.ts
@@ -0,0 +1,12 @@
+export default eventHandler(async (event) => {
+ return hubBlob().handleUpload(event, {
+ formKey: 'files', // read file or files form the `formKey` field of request body (body should be a `FormData` object)
+ multiple: true, // when `true`, the `formKey` field will be an array of `Blob` objects
+ ensure: {
+ types: ['image/jpeg', 'image/png'] // allowed types of the file
+ },
+ put: {
+ addRandomSuffix: true
+ }
+ })
+})
diff --git a/src/runtime/components/FileUpload.vue b/src/runtime/components/FileUpload.vue
new file mode 100644
index 00000000..354bf713
--- /dev/null
+++ b/src/runtime/components/FileUpload.vue
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/runtime/types/index.ts b/src/runtime/types/index.ts
index f413b71c..bb9f0951 100644
--- a/src/runtime/types/index.ts
+++ b/src/runtime/types/index.ts
@@ -20,6 +20,7 @@ export * from '../components/Container.vue'
export * from '../components/ContextMenu.vue'
export * from '../components/Drawer.vue'
export * from '../components/DropdownMenu.vue'
+export * from '../components/FileUpload.vue'
export * from '../components/Form.vue'
export * from '../components/FormField.vue'
export * from '../components/Icon.vue'
diff --git a/src/theme/file-upload.ts b/src/theme/file-upload.ts
new file mode 100644
index 00000000..ab5a7066
--- /dev/null
+++ b/src/theme/file-upload.ts
@@ -0,0 +1,5 @@
+export default {
+ slots: {
+ root: ''
+ }
+}
diff --git a/src/theme/index.ts b/src/theme/index.ts
index e03aecd3..3958378c 100644
--- a/src/theme/index.ts
+++ b/src/theme/index.ts
@@ -19,6 +19,7 @@ export { default as container } from './container'
export { default as contextMenu } from './context-menu'
export { default as drawer } from './drawer'
export { default as dropdownMenu } from './dropdown-menu'
+export { default as fileUpload } from './file-upload'
export { default as form } from './form'
export { default as formField } from './form-field'
export { default as input } from './input'
diff --git a/test/components/FileUpload.spec.ts b/test/components/FileUpload.spec.ts
new file mode 100644
index 00000000..0e8b60c1
--- /dev/null
+++ b/test/components/FileUpload.spec.ts
@@ -0,0 +1,18 @@
+import { describe, it, expect } from 'vitest'
+import FileUpload from '../../src/runtime/components/FileUpload.vue'
+import type { FileUploadProps, FileUploadSlots } from '../../src/runtime/components/FileUpload.vue'
+import ComponentRender from '../component-render'
+
+describe('FileUpload', () => {
+ it.each([
+ // Props
+ ['with as', { props: { as: 'section' } }],
+ ['with class', { props: { class: '' } }],
+ ['with ui', { props: { ui: {} } }],
+ // Slots
+ ['with default slot', { slots: { default: () => 'Default slot' } }]
+ ])('renders %s correctly', async (nameOrHtml: string, options: { props?: FileUploadProps, slots?: Partial
}) => {
+ const html = await ComponentRender(nameOrHtml, options, FileUpload)
+ expect(html).toMatchSnapshot()
+ })
+})