From e4fe3a6c6477835a8c5a587317ab72367583420a Mon Sep 17 00:00:00 2001 From: Etienne Delvarre Date: Fri, 29 May 2026 11:20:36 +0200 Subject: [PATCH] Vision layout overhaul: moodboard + expandable list items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace Inspirations with Moodboard (image cards with labels) - Tags and Moodboard on same row (two columns) - Ce qu'on veut/ne veut pas: items now support title + expandable description (click "développer" to add detail text) - Backwards compatible: old plain-string list items auto-migrate - Fix hero-meta default height to 400px - Moodboard images resized to 400px, JPEG 0.8 quality Co-Authored-By: Claude Opus 4.6 --- public/index.html | 213 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 148 insertions(+), 65 deletions(-) diff --git a/public/index.html b/public/index.html index c69c370..d0efa8d 100644 --- a/public/index.html +++ b/public/index.html @@ -242,18 +242,32 @@ .list-block h4.no { color: #a0523d; } .dark .list-block h4.yes { color: #8aaa6a; } .dark .list-block h4.no { color: #c07050; } - .list-item { padding: 8px 0; font-size: 15px; color: var(--text); display: flex; align-items: flex-start; gap: 8px; } + .list-item { padding: 10px 0; font-size: 15px; color: var(--text); display: flex; align-items: flex-start; gap: 8px; } .list-item .bullet { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; margin-top: 8px; } .list-item.yes .bullet { background: #5a7a3a; } .list-item.no .bullet { background: #a0523d; } .dark .list-item.yes .bullet { background: #8aaa6a; } .dark .list-item.no .bullet { background: #c07050; } + .list-item-title { font-weight: 600; } + .list-item-desc { font-size: 14px; color: var(--text-muted); line-height: 1.6; margin-top: 4px; } + .list-item.expanded { background: var(--bg-alt); border-radius: 8px; padding: 12px; border-left: 3px solid; margin: 4px 0; } + .list-item.expanded.yes { border-left-color: #5a7a3a; } + .list-item.expanded.no { border-left-color: #a0523d; } + .dark .list-item.expanded.yes { border-left-color: #8aaa6a; } + .dark .list-item.expanded.no { border-left-color: #c07050; } + .list-item-expand { font-size: 12px; color: var(--text-hint); cursor: pointer; margin-left: 8px; white-space: nowrap; } + .list-item-expand:hover { color: var(--accent); } - .inspi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; } - .inspi-card { background: var(--bg); border-radius: 10px; padding: 16px; text-align: center; border: 1px solid var(--border); } - .inspi-icon { font-size: 28px; margin-bottom: 8px; } - .inspi-title { font-size: 14px; font-weight: 500; color: var(--brown); } - .inspi-type { font-size: 13px; color: var(--text-hint); margin-top: 4px; } + .mood-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; } + .mood-card { position: relative; border-radius: 10px; overflow: hidden; aspect-ratio: 1; background: var(--bg-alt); border: 1px solid var(--border); cursor: pointer; display: flex; align-items: flex-end; background-size: cover; background-position: center; } + .mood-card.has-image { border-color: transparent; } + .mood-label { width: 100%; padding: 6px 10px; font-size: 12px; font-weight: 600; color: #fff; background: rgba(44,24,16,0.55); backdrop-filter: blur(4px); text-align: center; } + .mood-card:not(.has-image) .mood-label { color: var(--text-muted); background: transparent; } + .mood-card-remove { position: absolute; top: 4px; right: 4px; display: none; background: rgba(139,48,48,0.8); color: #fff; border: none; border-radius: 50%; width: 20px; height: 20px; font-size: 12px; cursor: pointer; align-items: center; justify-content: center; z-index: 2; } + .mood-card:hover .mood-card-remove { display: flex; } + .mood-add { border: 1px dashed var(--border); background: transparent; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; aspect-ratio: 1; border-radius: 10px; } + .mood-add:hover { background: var(--bg-alt); } + .mood-add-icon { font-size: 24px; color: var(--text-hint); } .vision-quote { font-family: 'Playfair Display', serif; font-size: 17px; font-style: italic; color: var(--text-muted); line-height: 1.7; border-left: 3px solid var(--accent); padding-left: 16px; margin: 12px 0; } @@ -280,9 +294,6 @@ .list-add { padding: 6px 0; font-size: 14px; color: var(--text-hint); cursor: pointer; display: flex; align-items: center; gap: 6px; } .list-add:hover { color: var(--accent); } - .inspi-card-remove { position: absolute; top: 4px; right: 4px; display: none; background: rgba(139,48,48,0.8); color: #fff; border: none; border-radius: 50%; width: 20px; height: 20px; font-size: 12px; cursor: pointer; align-items: center; justify-content: center; } - .inspi-card:hover .inspi-card-remove { display: flex; } - .inspi-card { position: relative; } /* Responsive */ @media (max-width: 768px) { @@ -382,6 +393,12 @@
+

Moodboard

+
+ +
+ +

Ce qu'on veut / ne veut pas

@@ -395,11 +412,6 @@
-
-

Inspirations et références

-
-
-
@@ -971,39 +983,87 @@ function removeTag(index) { renderTags(); } -// ── Lists (on-veut / on-ne-veut-pas) ── +// ── Lists (on-veut / on-ne-veut-pas) — items with title + optional description ── function renderList(section, containerId, cssClass) { const container = document.getElementById(containerId); const row = visionData[section]; let items = []; try { items = JSON.parse(row?.Contenu || '[]'); } catch { items = []; } + // Normalize: old format was plain strings, new format is {title, desc} + items = items.map(it => typeof it === 'string' ? { title: it, desc: '' } : it); container.innerHTML = ''; - items.forEach((text, i) => { + items.forEach((item, i) => { + const hasDesc = !!item.desc; const div = document.createElement('div'); - div.className = `list-item ${cssClass}`; - div.innerHTML = `${esc(text)}×`; - const textEl = div.querySelector('.list-item-text'); - textEl.addEventListener('blur', () => { - const newText = textEl.textContent.trim(); - if (newText && newText !== items[i]) { - items[i] = newText; - saveVisionSection(section, JSON.stringify(items)); - } + div.className = `list-item ${cssClass}${hasDesc ? ' expanded' : ''}`; + + const content = document.createElement('div'); + content.className = 'list-item-text'; + content.style.flex = '1'; + + const titleEl = document.createElement('div'); + titleEl.className = 'list-item-title'; + titleEl.contentEditable = 'true'; + titleEl.textContent = item.title; + titleEl.addEventListener('blur', () => { + const v = titleEl.textContent.trim(); + if (v && v !== items[i].title) { items[i].title = v; saveVisionSection(section, JSON.stringify(items)); } }); - textEl.addEventListener('keydown', e => { - if (e.key === 'Enter') { e.preventDefault(); textEl.blur(); } + titleEl.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); titleEl.blur(); } }); + content.appendChild(titleEl); + + const descEl = document.createElement('div'); + descEl.className = 'list-item-desc'; + descEl.contentEditable = 'true'; + descEl.textContent = item.desc || ''; + descEl.style.display = hasDesc ? 'block' : 'none'; + descEl.setAttribute('data-placeholder', 'Développer...'); + descEl.addEventListener('blur', () => { + const v = descEl.textContent.trim(); + if (v !== (items[i].desc || '')) { items[i].desc = v; saveVisionSection(section, JSON.stringify(items)); } + if (!v) { descEl.style.display = 'none'; div.classList.remove('expanded'); } }); + content.appendChild(descEl); + + const bullet = document.createElement('span'); + bullet.className = 'bullet'; + + const actions = document.createElement('span'); + actions.style.cssText = 'display:flex; gap:6px; align-items:center;'; + + const expandBtn = document.createElement('span'); + expandBtn.className = 'list-item-expand'; + expandBtn.textContent = hasDesc ? '' : 'développer →'; + expandBtn.style.display = hasDesc ? 'none' : ''; + expandBtn.onclick = () => { + descEl.style.display = 'block'; + div.classList.add('expanded'); + expandBtn.style.display = 'none'; + descEl.focus(); + }; + + const removeBtn = document.createElement('span'); + removeBtn.className = 'list-item-remove'; + removeBtn.innerHTML = '×'; + removeBtn.onclick = () => { items.splice(i, 1); saveVisionSection(section, JSON.stringify(items)); renderList(section, containerId, cssClass); }; + + actions.appendChild(expandBtn); + actions.appendChild(removeBtn); + + div.appendChild(bullet); + div.appendChild(content); + div.appendChild(actions); container.appendChild(div); }); const addEl = document.createElement('div'); addEl.className = 'list-add'; - addEl.innerHTML = '+ Ajouter'; + addEl.innerHTML = '+ Ajouter un élément'; addEl.onclick = () => { const text = prompt('Nouvel élément :'); if (!text) return; - items.push(text); + items.push({ title: text, desc: '' }); saveVisionSection(section, JSON.stringify(items)); renderList(section, containerId, cssClass); }; @@ -1019,61 +1079,84 @@ function removeListItem(section, containerId, cssClass, index) { renderList(section, containerId, cssClass); } -// ── Inspirations ── -const INSPI_ICONS = { 'Livre': '\u{1F4D6}', 'Film': '\u{1F3AC}', 'Peinture': '\u{1F3A8}', 'Musique': '\u{1F3B5}', 'Série': '\u{1F4FA}', 'Jeu': '\u{1F3AE}', 'Autre': '\u{2728}' }; - -function renderInspirations() { - const container = document.getElementById('v-inspirations'); - const row = visionData['inspirations']; +// ── Moodboard ── +function renderMoodboard() { + const container = document.getElementById('v-moodboard'); + const row = visionData['moodboard']; let items = []; try { items = JSON.parse(row?.Contenu || '[]'); } catch { items = []; } container.innerHTML = ''; items.forEach((item, i) => { const card = document.createElement('div'); - card.className = 'inspi-card'; + card.className = 'mood-card' + (item.image ? ' has-image' : ''); + if (item.image) card.style.backgroundImage = `url(${item.image})`; + card.innerHTML = ` - -
${item.icon || INSPI_ICONS[item.type] || '\u{2728}'}
-
${esc(item.title)}
-
${esc(item.type)}
+ +
${esc(item.label)}
`; - const titleEl = card.querySelector('.inspi-title'); - titleEl.addEventListener('blur', () => { - const newTitle = titleEl.textContent.trim(); - if (newTitle && newTitle !== items[i].title) { - items[i].title = newTitle; - saveVisionSection('inspirations', JSON.stringify(items)); - } - }); - titleEl.addEventListener('keydown', e => { - if (e.key === 'Enter') { e.preventDefault(); titleEl.blur(); } - }); + card.onclick = (e) => { + if (e.target.closest('.mood-card-remove')) return; + moodUploadTarget = i; + document.getElementById('mood-file-input').click(); + }; container.appendChild(card); }); const addCard = document.createElement('div'); - addCard.className = 'inspi-card'; - addCard.style.cssText = 'border:1px dashed var(--border); background:transparent; display:flex; flex-direction:column; align-items:center; justify-content:center; cursor:pointer;'; - addCard.innerHTML = '
+
Ajouter
'; + addCard.className = 'mood-add'; + addCard.innerHTML = '
+
'; addCard.onclick = () => { - const title = prompt('Titre de la référence :'); - if (!title) return; - const type = prompt('Type (Livre, Film, Peinture, Musique, Série, Jeu, Autre) :', 'Livre') || 'Livre'; - items.push({ title, type, icon: INSPI_ICONS[type] || '\u{2728}' }); - saveVisionSection('inspirations', JSON.stringify(items)); - renderInspirations(); + const label = prompt('Catégorie (ex : Ambiance, Architecture, Costumes...) :'); + if (!label) return; + items.push({ label, image: '' }); + saveVisionSection('moodboard', JSON.stringify(items)); + renderMoodboard(); }; container.appendChild(addCard); } -function removeInspiration(index) { - const row = visionData['inspirations']; +let moodUploadTarget = -1; +document.addEventListener('DOMContentLoaded', () => { + const moodInput = document.getElementById('mood-file-input'); + if (moodInput) moodInput.addEventListener('change', function() { + const file = this.files[0]; + if (!file || moodUploadTarget < 0) return; + const reader = new FileReader(); + reader.onload = (e) => { + const img = new Image(); + img.onload = () => { + const maxW = 400; + let w = img.width, h = img.height; + if (w > maxW) { h = Math.round(h * maxW / w); w = maxW; } + const canvas = document.createElement('canvas'); + canvas.width = w; canvas.height = h; + canvas.getContext('2d').drawImage(img, 0, 0, w, h); + const dataUrl = canvas.toDataURL('image/jpeg', 0.8); + const row = visionData['moodboard']; + let items = []; + try { items = JSON.parse(row?.Contenu || '[]'); } catch { items = []; } + if (items[moodUploadTarget]) { + items[moodUploadTarget].image = dataUrl; + saveVisionSection('moodboard', JSON.stringify(items)); + renderMoodboard(); + } + }; + img.src = e.target.result; + }; + reader.readAsDataURL(file); + this.value = ''; + }); +}); + +function removeMoodItem(index) { + const row = visionData['moodboard']; let items = []; try { items = JSON.parse(row?.Contenu || '[]'); } catch { return; } items.splice(index, 1); - saveVisionSection('inspirations', JSON.stringify(items)); - renderInspirations(); + saveVisionSection('moodboard', JSON.stringify(items)); + renderMoodboard(); } // ── Hero image with drag-to-reposition ── @@ -1197,7 +1280,7 @@ async function loadVision() { renderTags(); renderList('on-veut', 'v-on-veut', 'yes'); renderList('on-ne-veut-pas', 'v-on-ne-veut-pas', 'no'); - renderInspirations(); + renderMoodboard(); } catch (e) { console.error('Vision load error:', e); }