Update
This commit is contained in:
157
resources/js/projects-renderer/components/ProjectHero.vue
Normal file
157
resources/js/projects-renderer/components/ProjectHero.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<script setup>
|
||||
import { computed, inject } from 'vue';
|
||||
import { useImageUpload } from '../composables/useImageUpload';
|
||||
import { useIntersectionActivation } from '../composables/useIntersectionActivation';
|
||||
import { getBunnyEmbedUrl, getFrameIoEmbedUrl, getYouTubeEmbedUrl } from '../schema/projectSchema';
|
||||
|
||||
const props = defineProps({
|
||||
media: {
|
||||
type: Object,
|
||||
default: () => ({ type: 'image', url: '' }),
|
||||
},
|
||||
});
|
||||
|
||||
const { isActive, target } = useIntersectionActivation();
|
||||
|
||||
const youtubeUrl = computed(() => getYouTubeEmbedUrl(props.media));
|
||||
const frameIoUrl = computed(() => getFrameIoEmbedUrl(props.media?.url));
|
||||
const bunnyUrl = computed(() => getBunnyEmbedUrl(props.media));
|
||||
|
||||
const uploadUrl = inject('editorUploadUrl', null);
|
||||
const updateHeroImage = inject('editorUpdateHeroImage', null);
|
||||
|
||||
const { isDragOver, isUploading, fileInputRef, onDragOver, onDragLeave, onDrop, openPicker, onFileSelected } = useImageUpload(
|
||||
() => uploadUrl?.value ?? null,
|
||||
(url) => updateHeroImage?.(url),
|
||||
);
|
||||
|
||||
const isDroppable = computed(() => props.media?.type === 'image' && !!uploadUrl?.value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
ref="target"
|
||||
class="project-hero"
|
||||
:class="{ 'project-hero--over': isDragOver, 'project-hero--uploading': isUploading, 'project-hero--editable': isDroppable }"
|
||||
@dragover="isDroppable ? onDragOver($event) : null"
|
||||
@dragleave="onDragLeave"
|
||||
@drop="isDroppable ? onDrop($event) : null"
|
||||
@click="isDroppable ? openPicker() : null"
|
||||
>
|
||||
<iframe
|
||||
v-if="media.type === 'youtube' && isActive && youtubeUrl"
|
||||
:src="youtubeUrl"
|
||||
title="Project hero video"
|
||||
loading="lazy"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
/>
|
||||
|
||||
<iframe
|
||||
v-else-if="media.type === 'frameio' && isActive && frameIoUrl"
|
||||
:src="frameIoUrl"
|
||||
title="Project hero video"
|
||||
loading="lazy"
|
||||
allow="autoplay; fullscreen; picture-in-picture"
|
||||
allowfullscreen
|
||||
/>
|
||||
|
||||
<iframe
|
||||
v-else-if="media.type === 'bunny' && isActive && bunnyUrl"
|
||||
:src="bunnyUrl"
|
||||
title="Project hero video"
|
||||
loading="lazy"
|
||||
style="border:0;position:absolute;top:0;height:100%;width:100%;"
|
||||
allow="accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture;"
|
||||
allowfullscreen
|
||||
/>
|
||||
|
||||
<video
|
||||
v-else-if="media.type === 'video' && isActive && media.url"
|
||||
:autoplay="media.autoplay === true"
|
||||
:muted="media.muted === true"
|
||||
:loop="media.loop === true"
|
||||
controls
|
||||
playsinline
|
||||
preload="metadata"
|
||||
>
|
||||
<source :src="media.url">
|
||||
</video>
|
||||
|
||||
<img v-else-if="media.url" :src="media.url" alt="Project hero" loading="lazy" :class="{ 'project-hero__img--uploading': isUploading }">
|
||||
|
||||
<div v-else class="project-hero__placeholder">
|
||||
{{ isUploading ? 'Uploading…' : (isDroppable ? 'Drop image here or click to upload.' : 'Add a YouTube URL, Frame.io review link, Bunny embed URL, native video URL, or image URL.') }}
|
||||
</div>
|
||||
|
||||
<div v-if="isDragOver" class="project-hero__overlay">Drop to upload</div>
|
||||
<div v-if="isUploading && media.url" class="project-hero__overlay project-hero__overlay--uploading">Uploading…</div>
|
||||
</section>
|
||||
|
||||
<input ref="fileInputRef" type="file" accept="image/*" style="display:none" @change="onFileSelected">
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.project-hero {
|
||||
aspect-ratio: 16 / 9;
|
||||
background: linear-gradient(135deg, rgba(15, 23, 42, 0.08), rgba(148, 163, 184, 0.18));
|
||||
border: 1px solid rgba(15, 23, 42, 0.08);
|
||||
border-radius: 1.75rem;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: outline-color 0.15s;
|
||||
}
|
||||
|
||||
.project-hero--editable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.project-hero--over {
|
||||
outline: 2px dashed rgba(14, 116, 144, 0.7);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
iframe,
|
||||
video,
|
||||
img {
|
||||
border: 0;
|
||||
display: block;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.project-hero__img--uploading {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.project-hero__placeholder {
|
||||
align-items: center;
|
||||
color: var(--project-muted, #6b7280);
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.project-hero__overlay {
|
||||
align-items: center;
|
||||
background: rgba(14, 116, 144, 0.55);
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.project-hero__overlay--uploading {
|
||||
background: rgba(15, 23, 42, 0.4);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user