|
68 | 68 | }>(); |
69 | 69 | const form = useParentForm(); |
70 | 70 | const transformedImg = ref<string>(); |
| 71 | + const playablePreviewUrl = ref<string>(); |
71 | 72 | const uppyFile = ref<UppyFile<{}, {}>>(); |
72 | 73 | const isEditable = computed(() => { |
73 | 74 | return props.value && canTransform(props.value.name, props.value.mime_type) && !props.hasError |
|
135 | 136 | const blob = await response.blob(); |
136 | 137 | transformedImg.value = URL.createObjectURL(blob); |
137 | 138 | } |
| 139 | + } else if(file.type?.startsWith('video/') || file.type?.startsWith('audio/')) { |
| 140 | + playablePreviewUrl.value = URL.createObjectURL(file.data); |
138 | 141 | } |
139 | 142 | }) |
140 | 143 | .on('restriction-failed', (file, error) => { |
|
350 | 353 | uppy.removeFile(uppyFile.value.id); |
351 | 354 | uppyFile.value = null; |
352 | 355 | transformedImg.value = null; |
| 356 | + playablePreviewUrl.value = null; |
353 | 357 | editModalImageUrl.value = null; |
354 | 358 | } |
355 | 359 | } |
|
382 | 386 | if(!props.persistThumbnailUrl && transformedImg.value) { |
383 | 387 | URL.revokeObjectURL(transformedImg.value); |
384 | 388 | } |
| 389 | + if(playablePreviewUrl.value) { |
| 390 | + URL.revokeObjectURL(playablePreviewUrl.value); |
| 391 | + } |
385 | 392 | emit('uploading', false); |
386 | 393 | }); |
387 | 394 | </script> |
|
392 | 399 | <template v-if="value?.path || value?.uploaded || uppyFile"> |
393 | 400 | <div :class="{ 'bg-background border rounded-md p-4': !asEditorEmbed }"> |
394 | 401 | <div class="flex gap-4"> |
395 | | - <template v-if="transformedImg ?? value?.thumbnail ?? uppyFile?.preview"> |
396 | | - <div class="self-center group/img relative rounded-md overflow-hidden"> |
397 | | - <img class="rounded-md min-w-[50px] max-h-[150px] max-w-[150px] object-contain" |
398 | | - :class="uppyFile && !transformedImg && field.imageCropRatio ? 'object-cover aspect-(--ratio)' : ''" |
399 | | - :style="{ |
| 402 | + <div class="flex-1 min-w-0 flex gap-4" :class="playablePreviewUrl ?? value?.playable_preview_url ? 'flex-col' : ''"> |
| 403 | + <template v-if="transformedImg ?? value?.thumbnail ?? uppyFile?.preview"> |
| 404 | + <div class="self-center group/img relative rounded-md overflow-hidden"> |
| 405 | + <img class="rounded-md min-w-[50px] max-h-[150px] max-w-[150px] object-contain" |
| 406 | + :class="uppyFile && !transformedImg && field.imageCropRatio ? 'object-cover aspect-(--ratio)' : ''" |
| 407 | + :style="{ |
400 | 408 | '--ratio': field.imageCropRatio ? `${field.imageCropRatio[0]} / ${field.imageCropRatio[1]}` : null |
401 | 409 | }" |
402 | | - :src="transformedImg ?? value?.thumbnail ?? uppyFile.preview" |
403 | | - :alt="value?.name ?? uppyFile?.name" |
404 | | - > |
405 | | - <template v-if="isEditable && !props.field.readOnly"> |
406 | | - <button class="absolute flex justify-center items-center gap-2 inset-0 bg-black/50 transition text-white text-xs font-medium opacity-0 group-hover/img:opacity-100" tabindex="-1" @click="onEdit"> |
407 | | - {{ __('sharp::form.upload.edit_button') }} |
408 | | - </button> |
| 410 | + :src="transformedImg ?? value?.thumbnail ?? uppyFile.preview" |
| 411 | + :alt="value?.name ?? uppyFile?.name" |
| 412 | + > |
| 413 | + <template v-if="isEditable && !props.field.readOnly"> |
| 414 | + <button class="absolute flex justify-center items-center gap-2 inset-0 bg-black/50 transition text-white text-xs font-medium opacity-0 group-hover/img:opacity-100" tabindex="-1" @click="onEdit"> |
| 415 | + {{ __('sharp::form.upload.edit_button') }} |
| 416 | + </button> |
| 417 | + </template> |
| 418 | + </div> |
| 419 | + </template> |
| 420 | + <template v-else-if="playablePreviewUrl ?? value?.playable_preview_url"> |
| 421 | + <template v-if="(uppyFile?.type ?? value?.mime_type)?.startsWith('video/')"> |
| 422 | + <video class="rounded-md max-h-[150px]" controls> |
| 423 | + <source :src="playablePreviewUrl ?? value.playable_preview_url" :type="uppyFile?.type ?? value?.mime_type"> |
| 424 | + </video> |
409 | 425 | </template> |
410 | | - </div> |
411 | | - </template> |
412 | | - <template v-else> |
413 | | - <FileIcon class="self-center size-4" :mime-type="value?.mime_type || uppyFile?.type" /> |
414 | | - </template> |
415 | | - <div class="flex-1 min-w-0"> |
416 | | - <div class="text-sm truncate"> |
417 | | - <template v-if="value?.path"> |
418 | | - <TooltipProvider> |
419 | | - <Tooltip :delay-duration="0" disable-hoverable-content> |
420 | | - <TooltipTrigger as-child> |
421 | | - <a class="text-foreground underline underline-offset-4 decoration-foreground/20 hover:decoration-foreground" |
422 | | - :href="route('code16.sharp.download.show', { |
| 426 | + <template v-else-if="(uppyFile?.type ?? value?.mime_type)?.startsWith('audio/')"> |
| 427 | + <audio controls> |
| 428 | + <source :src="playablePreviewUrl ?? value.playable_preview_url" :type="uppyFile?.type ?? value?.mime_type"> |
| 429 | + </audio> |
| 430 | + </template> |
| 431 | + </template> |
| 432 | + <template v-else> |
| 433 | + <FileIcon class="self-center size-4" :mime-type="value?.mime_type || uppyFile?.type" /> |
| 434 | + </template> |
| 435 | + <div class="flex-1 min-w-0"> |
| 436 | + <div class="text-sm truncate"> |
| 437 | + <template v-if="value?.path"> |
| 438 | + <TooltipProvider> |
| 439 | + <Tooltip :delay-duration="0" disable-hoverable-content> |
| 440 | + <TooltipTrigger as-child> |
| 441 | + <a class="text-foreground underline underline-offset-4 decoration-foreground/20 hover:decoration-foreground" |
| 442 | + :href="route('code16.sharp.download.show', { |
423 | 443 | entityKey: form.entityKey, |
424 | 444 | instanceId: form.instanceId, |
425 | 445 | disk: value.disk, |
426 | 446 | path: value.path, |
427 | 447 | })" |
428 | | - :download="value?.name" |
429 | | - > |
430 | | - {{ value?.name }} |
431 | | - </a> |
432 | | - </TooltipTrigger> |
| 448 | + :download="value?.name" |
| 449 | + > |
| 450 | + {{ value?.name }} |
| 451 | + </a> |
| 452 | + </TooltipTrigger> |
433 | 453 |
|
434 | | - <TooltipContent class="pointer-events-none" :side-offset="10"> |
435 | | - {{ __('sharp::form.upload.download_tooltip') }} |
436 | | - </TooltipContent> |
437 | | - </Tooltip> |
438 | | - </TooltipProvider> |
| 454 | + <TooltipContent class="pointer-events-none" :side-offset="10"> |
| 455 | + {{ __('sharp::form.upload.download_tooltip') }} |
| 456 | + </TooltipContent> |
| 457 | + </Tooltip> |
| 458 | + </TooltipProvider> |
| 459 | + </template> |
| 460 | + <template v-else> |
| 461 | + {{ value?.name ?? uppyFile?.name }} |
| 462 | + </template> |
| 463 | + </div> |
| 464 | + <template v-if="value?.size ?? uppyFile?.size"> |
| 465 | + <div class="mt-2 text-xs text-muted-foreground"> |
| 466 | + {{ filesizeLabel(value?.size ?? uppyFile.size) }} |
| 467 | + </div> |
439 | 468 | </template> |
440 | | - <template v-else> |
441 | | - {{ value?.name ?? uppyFile?.name }} |
| 469 | + <template v-if="legend"> |
| 470 | + <div class="mt-2 text-xs">{{ legend }}</div> |
442 | 471 | </template> |
443 | | - </div> |
444 | | - <template v-if="value?.size ?? uppyFile?.size"> |
445 | | - <div class="mt-2 text-xs text-muted-foreground"> |
446 | | - {{ filesizeLabel(value?.size ?? uppyFile.size) }} |
447 | | - </div> |
448 | | - </template> |
449 | | - <template v-if="legend"> |
450 | | - <div class="mt-2 text-xs">{{ legend }}</div> |
451 | | - </template> |
452 | | - <template v-if="uppyFile?.progress.percentage < 100 && !hasError"> |
453 | | - <div class="mt-2"> |
454 | | - <div class="bg-primary h-0.5 transition-all" :style="{ width: `${uppyFile.progress.percentage}%` }" role="progressbar"> |
| 472 | + <template v-if="uppyFile?.progress.percentage < 100 && !hasError"> |
| 473 | + <div class="mt-2"> |
| 474 | + <div class="bg-primary h-0.5 transition-all" :style="{ width: `${uppyFile.progress.percentage}%` }" role="progressbar"> |
| 475 | + </div> |
455 | 476 | </div> |
456 | | - </div> |
457 | | - </template> |
| 477 | + </template> |
| 478 | + </div> |
458 | 479 | </div> |
459 | 480 | <DropdownMenu :modal="false"> |
460 | 481 | <DropdownMenuTrigger as-child> |
|
0 commit comments