Ajoute un accès direct à la galerie en plus de la caméra
Deux boutons séparés (deux inputs file) plutôt qu'un seul avec capture=environment qui masquait l'option galerie — retour de Kévin après test réel. État de sélection centralisé dans state.selectedFile.
This commit is contained in:
+19
-5
@@ -36,11 +36,15 @@
|
|||||||
|
|
||||||
<form id="upload-form" class="upload-form" hidden novalidate>
|
<form id="upload-form" class="upload-form" hidden novalidate>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="photo-input">Ta photo</label>
|
<label id="photo-field-label">Ta photo</label>
|
||||||
<!-- capture="environment" : ouvre directement l'appareil photo
|
<!-- Deux entrées séparées : "photo-input" (capture="environment")
|
||||||
(caméra arrière) sur mobile plutôt que la galerie — décision
|
ouvre directement l'appareil photo, "photo-input-gallery" ouvre
|
||||||
actée avec Kévin le 2026-09-16, remplace le choix initial qui
|
le sélecteur de fichiers classique. Décision actée avec Kévin le
|
||||||
laissait le choix caméra/galerie. -->
|
2026-09-16 : les deux doivent être accessibles en un tap. -->
|
||||||
|
<div class="photo-source-buttons">
|
||||||
|
<label for="photo-input" class="btn-secondary">📷 Prendre une photo</label>
|
||||||
|
<label for="photo-input-gallery" class="btn-secondary">🖼️ Depuis la galerie</label>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
id="photo-input"
|
id="photo-input"
|
||||||
@@ -48,7 +52,17 @@
|
|||||||
accept="image/*,.heic,.heif"
|
accept="image/*,.heic,.heif"
|
||||||
capture="environment"
|
capture="environment"
|
||||||
required
|
required
|
||||||
|
class="visually-hidden"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="photo-input-gallery"
|
||||||
|
accept="image/*,.heic,.heif"
|
||||||
|
class="visually-hidden"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<p id="file-chosen-label" class="file-chosen-label">Aucun fichier choisi</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img id="preview" class="preview" alt="Aperçu de la photo choisie" hidden>
|
<img id="preview" class="preview" alt="Aperçu de la photo choisie" hidden>
|
||||||
|
|||||||
@@ -112,6 +112,50 @@ input[type="file"] {
|
|||||||
padding: 0.55rem 0.5rem;
|
padding: 0.55rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.visually-hidden {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-source-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding: 0.85rem 0.6rem;
|
||||||
|
min-height: 48px;
|
||||||
|
border: 1px solid var(--color-accent);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: var(--color-accent);
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-chosen-label {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 4.5rem;
|
min-height: 4.5rem;
|
||||||
|
|||||||
+24
-4
@@ -40,6 +40,8 @@ const successBlock = document.getElementById("success-block");
|
|||||||
const addAnotherBtn = document.getElementById("add-another-btn");
|
const addAnotherBtn = document.getElementById("add-another-btn");
|
||||||
const form = document.getElementById("upload-form");
|
const form = document.getElementById("upload-form");
|
||||||
const photoInput = document.getElementById("photo-input");
|
const photoInput = document.getElementById("photo-input");
|
||||||
|
const photoInputGallery = document.getElementById("photo-input-gallery");
|
||||||
|
const fileChosenLabel = document.getElementById("file-chosen-label");
|
||||||
const previewImg = document.getElementById("preview");
|
const previewImg = document.getElementById("preview");
|
||||||
const nomInput = document.getElementById("nom-input");
|
const nomInput = document.getElementById("nom-input");
|
||||||
const messageInput = document.getElementById("message-input");
|
const messageInput = document.getElementById("message-input");
|
||||||
@@ -413,17 +415,20 @@ async function insertPhotoRow(eventId, path, nom, message) {
|
|||||||
|
|
||||||
let previewObjectUrl = null;
|
let previewObjectUrl = null;
|
||||||
|
|
||||||
photoInput.addEventListener("change", () => {
|
// Deux inputs (caméra directe / galerie) partagent la même logique de
|
||||||
|
// sélection — état centralisé dans state.selectedFile plutôt que de lire
|
||||||
|
// photoInput.files[0] un peu partout, pour rester correct quel que soit
|
||||||
|
// l'input utilisé.
|
||||||
|
function handleFileSelected(file) {
|
||||||
// Nouveau fichier choisi -> on invalide toute compression déjà faite.
|
// Nouveau fichier choisi -> on invalide toute compression déjà faite.
|
||||||
state.compressedBlob = null;
|
state.compressedBlob = null;
|
||||||
state.selectedFile = photoInput.files[0] || null;
|
state.selectedFile = file || null;
|
||||||
|
|
||||||
if (previewObjectUrl) {
|
if (previewObjectUrl) {
|
||||||
URL.revokeObjectURL(previewObjectUrl);
|
URL.revokeObjectURL(previewObjectUrl);
|
||||||
previewObjectUrl = null;
|
previewObjectUrl = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = state.selectedFile;
|
|
||||||
if (file && file.type && file.type.startsWith("image/")) {
|
if (file && file.type && file.type.startsWith("image/")) {
|
||||||
previewObjectUrl = URL.createObjectURL(file);
|
previewObjectUrl = URL.createObjectURL(file);
|
||||||
previewImg.src = previewObjectUrl;
|
previewImg.src = previewObjectUrl;
|
||||||
@@ -435,6 +440,20 @@ photoInput.addEventListener("change", () => {
|
|||||||
// Pas d'aperçu fiable pour les HEIC sur la plupart des navigateurs.
|
// Pas d'aperçu fiable pour les HEIC sur la plupart des navigateurs.
|
||||||
previewImg.hidden = true;
|
previewImg.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileChosenLabel.textContent = file ? file.name : "Aucun fichier choisi";
|
||||||
|
}
|
||||||
|
|
||||||
|
photoInput.addEventListener("change", () => {
|
||||||
|
handleFileSelected(photoInput.files[0] || null);
|
||||||
|
// Un seul fichier actif à la fois : on vide l'autre input pour éviter
|
||||||
|
// toute ambiguïté si l'utilisateur change de méthode après coup.
|
||||||
|
photoInputGallery.value = "";
|
||||||
|
});
|
||||||
|
|
||||||
|
photoInputGallery.addEventListener("change", () => {
|
||||||
|
handleFileSelected(photoInputGallery.files[0] || null);
|
||||||
|
photoInput.value = "";
|
||||||
});
|
});
|
||||||
|
|
||||||
async function submitPhoto() {
|
async function submitPhoto() {
|
||||||
@@ -444,7 +463,7 @@ async function submitPhoto() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = photoInput.files[0];
|
const file = state.selectedFile;
|
||||||
if (!file) return showError(appError("file-missing"));
|
if (!file) return showError(appError("file-missing"));
|
||||||
if (!isAcceptableImageFile(file)) return showError(appError("file-not-image"));
|
if (!isAcceptableImageFile(file)) return showError(appError("file-not-image"));
|
||||||
if (file.size > MAX_INPUT_BYTES) return showError(appError("file-too-large"));
|
if (file.size > MAX_INPUT_BYTES) return showError(appError("file-too-large"));
|
||||||
@@ -487,6 +506,7 @@ addAnotherBtn.addEventListener("click", () => {
|
|||||||
form.reset();
|
form.reset();
|
||||||
state.selectedFile = null;
|
state.selectedFile = null;
|
||||||
state.compressedBlob = null;
|
state.compressedBlob = null;
|
||||||
|
fileChosenLabel.textContent = "Aucun fichier choisi";
|
||||||
previewImg.hidden = true;
|
previewImg.hidden = true;
|
||||||
if (previewObjectUrl) {
|
if (previewObjectUrl) {
|
||||||
URL.revokeObjectURL(previewObjectUrl);
|
URL.revokeObjectURL(previewObjectUrl);
|
||||||
|
|||||||
Reference in New Issue
Block a user