Skip to content

Commit 69906bc

Browse files
benjamincanacatinux
andcommitted
fix(FileUpload): use native img element for blob URLs preview
Resolves #5121, resolves #4824 Co-Authored-By: Sébastien Chopin <[email protected]>
1 parent 63b26b2 commit 69906bc

File tree

6 files changed

+33
-6
lines changed

6 files changed

+33
-6
lines changed

playgrounds/nuxt/app/pages/components/file-upload.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ async function onSubmit(event: FormSubmitEvent<schema>) {
9191
<USelect v-model="position" :items="positions" placeholder="Position" />
9292
</Navbar>
9393

94-
<Matrix v-slot="props" :attrs="attrs">
94+
<Matrix v-slot="props" :attrs="attrs" container-class="w-80">
9595
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
9696
<UFormField name="avatar" label="Avatar" description="JPG, GIF or PNG. 1MB Max." v-bind="props">
9797
<UFileUpload v-slot="{ open, removeFile }" v-model="state.avatar" accept="image/*">

src/runtime/components/Avatar.vue

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface AvatarProps {
1111
* The element or component this component should render as.
1212
* @defaultValue 'span'
1313
*/
14-
as?: any
14+
as?: any | { root?: any, img?: any }
1515
src?: string
1616
alt?: string
1717
/**
@@ -37,6 +37,7 @@ export interface AvatarSlots {
3737
<script setup lang="ts">
3838
import { ref, computed, watch } from 'vue'
3939
import { Primitive, Slot } from 'reka-ui'
40+
import { defu } from 'defu'
4041
import { useAppConfig } from '#imports'
4142
import ImageComponent from '#build/ui-image-component'
4243
import { useAvatarGroup } from '../composables/useAvatarGroup'
@@ -46,7 +47,15 @@ import UChip from './Chip.vue'
4647
4748
defineOptions({ inheritAttrs: false })
4849
49-
const props = withDefaults(defineProps<AvatarProps>(), { as: 'span' })
50+
const props = defineProps<AvatarProps>()
51+
52+
const as = computed(() => {
53+
if (typeof props.as === 'string' || typeof props.as?.render === 'function') {
54+
return { root: props.as }
55+
}
56+
57+
return defu(props.as, { root: 'span' })
58+
})
5059
5160
const fallback = computed(() => props.text || (props.alt || '').split(' ').map(word => word.charAt(0)).join('').substring(0, 2))
5261
@@ -86,13 +95,13 @@ function onError() {
8695
<template>
8796
<component
8897
:is="props.chip ? UChip : Primitive"
89-
:as="as"
98+
:as="as.root"
9099
v-bind="props.chip ? (typeof props.chip === 'object' ? { inset: true, ...props.chip } : { inset: true }) : {}"
91100
:class="ui.root({ class: [props.ui?.root, props.class] })"
92101
:style="props.style"
93102
>
94103
<component
95-
:is="ImageComponent"
104+
:is="as.img || ImageComponent"
96105
v-if="src && !error"
97106
:src="src"
98107
:alt="alt"

src/runtime/components/FileUpload.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ defineExpose({
273273
<div v-for="(file, index) in Array.isArray(modelValue) ? modelValue : [modelValue]" :key="(file as File).name" :class="ui.file({ class: props.ui?.file })">
274274
<slot name="file" :file="file" :index="index">
275275
<slot name="file-leading" :file="file" :index="index">
276-
<UAvatar :src="createObjectUrl(file)" :icon="fileIcon || appConfig.ui.icons.file" :size="props.size" :class="ui.fileLeadingAvatar({ class: props.ui?.fileLeadingAvatar })" />
276+
<UAvatar :as="{ img: 'img' }" :src="createObjectUrl(file)" :icon="fileIcon || appConfig.ui.icons.file" :size="props.size" :class="ui.fileLeadingAvatar({ class: props.ui?.fileLeadingAvatar })" />
277277
</slot>
278278

279279
<div :class="ui.fileWrapper({ class: props.ui?.fileWrapper })">

test/components/Avatar.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ describe('Avatar', () => {
1616
['with chip', { props: { chip: { text: '1' } } }],
1717
...sizes.map((size: string) => [`with size ${size}`, { props: { src: 'https://github.com/benjamincanac.png', size } }]),
1818
['with as', { props: { as: 'section' } }],
19+
['with as (object)', { props: { src: 'https://github.com/benjamincanac.png', as: { root: 'section', img: 'p' } } }],
20+
['with as (partial object)', { props: { src: 'https://github.com/benjamincanac.png', as: { img: 'p' } } }],
1921
['with class', { props: { class: 'bg-default' } }],
2022
['with ui', { props: { ui: { fallback: 'font-bold' } } }],
2123
// Slots

test/components/__snapshots__/Avatar-vue.spec.ts.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
exports[`Avatar > renders with alt correctly 1`] = `"<span class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><span class="font-medium leading-none text-muted truncate">BC</span></span>"`;
44

5+
exports[`Avatar > renders with as (object) correctly 1`] = `
6+
"<section class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base">
7+
<p src="https://github.com/benjamincanac.png" width="32" height="32" class="h-full w-full rounded-[inherit] object-cover"></p>
8+
</section>"
9+
`;
10+
11+
exports[`Avatar > renders with as (partial object) correctly 1`] = `"<span class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><p src="https://github.com/benjamincanac.png" width="32" height="32" class="h-full w-full rounded-[inherit] object-cover"></p></span>"`;
12+
513
exports[`Avatar > renders with as correctly 1`] = `"<section class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><span class="font-medium leading-none text-muted truncate">&nbsp;</span></section>"`;
614

715
exports[`Avatar > renders with chip correctly 1`] = `"<span class="relative inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><span class="font-medium leading-none text-muted truncate">&nbsp;</span><span class="rounded-full ring ring-bg flex items-center justify-center text-inverted font-medium whitespace-nowrap bg-primary h-[8px] min-w-[8px] text-[8px] top-0 right-0 absolute">1</span></span>"`;

test/components/__snapshots__/Avatar.spec.ts.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
exports[`Avatar > renders with alt correctly 1`] = `"<span class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><span class="font-medium leading-none text-muted truncate">BC</span></span>"`;
44

5+
exports[`Avatar > renders with as (object) correctly 1`] = `
6+
"<section class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base">
7+
<p src="https://github.com/benjamincanac.png" width="32" height="32" class="h-full w-full rounded-[inherit] object-cover"></p>
8+
</section>"
9+
`;
10+
11+
exports[`Avatar > renders with as (partial object) correctly 1`] = `"<span class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><p src="https://github.com/benjamincanac.png" width="32" height="32" class="h-full w-full rounded-[inherit] object-cover"></p></span>"`;
12+
513
exports[`Avatar > renders with as correctly 1`] = `"<section class="inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><span class="font-medium leading-none text-muted truncate">&nbsp;</span></section>"`;
614

715
exports[`Avatar > renders with chip correctly 1`] = `"<span class="relative inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated size-8 text-base"><span class="font-medium leading-none text-muted truncate">&nbsp;</span><span class="rounded-full ring ring-bg flex items-center justify-center text-inverted font-medium whitespace-nowrap bg-primary h-[8px] min-w-[8px] text-[8px] top-0 right-0 absolute">1</span></span>"`;

0 commit comments

Comments
 (0)