Files
aritmija/resources/js/projects-renderer/components/ProjectMetadata.vue
2026-05-13 17:11:09 +02:00

210 lines
5.2 KiB
Vue

<script setup>
import { computed, inject } from 'vue';
defineProps({
metadata: {
type: Object,
default: () => ({ clientName: '', awarded: [], description: '' }),
},
mobilePreview: {
type: Boolean,
default: false,
},
});
const updateMetadata = inject('editorUpdateMetadata', null);
const isEditable = computed(() => !!updateMetadata);
function onClientNameBlur(e) {
updateMetadata?.('clientName', e.target.innerText.trim());
}
function onAwardedBlur(e) {
const lines = e.target.innerText.split('\n').map((s) => s.trim()).filter(Boolean);
updateMetadata?.('awarded', lines);
}
function onDescriptionBlur(e) {
updateMetadata?.('description', e.target.innerHTML.trim());
}
</script>
<template>
<section class="project-metadata" :class="{ 'project-metadata--mobile': mobilePreview }">
<!-- CLIENT -->
<article :class="{ 'project-metadata__article--editable': isEditable }">
<span>Client</span>
<strong
v-if="isEditable"
contenteditable="true"
class="project-metadata__editable"
:data-placeholder="'Add client name'"
@blur="onClientNameBlur"
v-text="metadata.clientName || ''"
></strong>
<strong v-else>{{ metadata.clientName || 'Add client name' }}</strong>
</article>
<!-- AWARDED -->
<article :class="{ 'project-metadata__article--editable': isEditable }">
<span>Awarded</span>
<div
v-if="isEditable"
contenteditable="true"
class="project-metadata__editable project-metadata__awarded-edit"
:data-placeholder="'Add award lines'"
@blur="onAwardedBlur"
v-text="(metadata.awarded || []).join('\n') || ''"
></div>
<template v-else>
<ul v-if="metadata.awarded?.length">
<li v-for="award in metadata.awarded" :key="award">{{ award }}</li>
</ul>
<p v-else>Add award lines</p>
</template>
</article>
<!-- DESCRIPTION -->
<article class="project-metadata__full" :class="{ 'project-metadata__article--editable': isEditable }">
<span>Description</span>
<div
v-if="isEditable"
contenteditable="true"
class="project-metadata__editable project-metadata__description-edit"
:data-placeholder="'Add a project description to populate this summary row.'"
@blur="onDescriptionBlur"
v-html="metadata.description || ''"
></div>
<div v-else-if="metadata.description" class="project-metadata__description" v-html="metadata.description"></div>
<p v-else>Add a project description to populate this summary row.</p>
</article>
</section>
</template>
<style scoped>
.project-metadata {
container-type: inline-size;
display: grid;
gap: 1.25rem;
grid-template-columns: 1fr 1fr;
}
.project-metadata article:last-child {
grid-column: 1 / -1;
}
.project-metadata__full {
grid-column: 1 / -1;
}
.project-metadata--mobile {
grid-template-columns: 1fr;
}
.project-metadata--mobile .project-metadata__full {
grid-column: auto;
}
article {
background: var(--project-surface, rgba(255, 255, 255, 0.74));
border: 1px solid rgba(15, 23, 42, 0.08);
border-radius: 1.35rem;
display: grid;
gap: 0.85rem;
padding: 1.35rem;
}
.project-metadata__article--editable {
cursor: text;
transition: border-color 0.15s, box-shadow 0.15s;
}
.project-metadata__article--editable:hover {
border-color: rgba(15, 23, 42, 0.18);
box-shadow: 0 0 0 3px rgba(14, 116, 144, 0.06);
}
.project-metadata__article--editable:focus-within {
border-color: rgba(14, 116, 144, 0.45);
box-shadow: 0 0 0 3px rgba(14, 116, 144, 0.12);
}
.project-metadata__editable {
outline: none;
}
.project-metadata__editable:empty::before {
color: var(--project-muted, #6b7280);
content: attr(data-placeholder);
pointer-events: none;
}
.project-metadata__awarded-edit {
color: var(--project-ink, #111827);
font-size: 1rem;
line-height: 1.6;
white-space: pre-wrap;
}
.project-metadata__description-edit {
color: var(--project-ink, #111827);
line-height: 1.6;
}
span {
color: var(--project-muted, #6b7280);
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
}
strong,
p,
li {
color: var(--project-ink, #111827);
line-height: 1.6;
}
strong {
font-size: 1.05rem;
}
ul,
p {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
@container (max-width: 600px) {
.project-metadata {
grid-template-columns: 1fr;
}
.project-metadata article:last-child {
grid-column: auto;
}
.project-metadata__full {
grid-column: auto;
}
}
@media (max-width: 900px) {
.project-metadata {
grid-template-columns: 1fr;
}
.project-metadata article:last-child {
grid-column: auto;
}
.project-metadata__full {
grid-column: auto;
}
}
</style>