Merge pull request #173 from IliesAllali/feat/visages-discrets

Visages sur la photo : rien par défaut, ronds fins à la demande
This commit is contained in:
IliesAllali authored and GitHub committed 2026-10-27 15:05:36 +01:00
commit b866b36ca9
3 files changed
+40 -15

No files matched your search

+15 -8
View File
@@ -3,19 +3,27 @@
.mp-avatar { width: 24px; height: 24px; flex: none; border-radius: 50%; object-fit: cover; }
.mp-avatar--initial { display: grid; place-items: center; background: var(--pz-a100); color: var(--pz-a700); font: 400 12px/1 var(--pz-serif); }
/* Point sur un visage : rond blanc cerclé, le prénom apparaît au survol ou au focus (toujours en identification) */
/* Rond sur un visage : fil fin, assez large pour entourer le visage sans le couvrir. Caché par défaut, la photo d'abord
(Patch, 27/10) : visible au survol de la photo, quand on la touche (.show-names), quand son prénom est survolé
dessous (.is-hot), et toujours en identification. Apparition d'un coup, sans fondu. */
.mp-marker {
position: absolute;
width: 26px;
height: 26px;
margin: -13px 0 0 -13px;
width: 34px;
height: 34px;
margin: -17px 0 0 -17px;
padding: 0;
border: 2px solid #fff;
border: 1.5px solid rgba(255, 255, 255, .9);
border-radius: 50%;
background: transparent;
box-shadow: 0 0 0 1px rgba(42, 38, 34, .3), 0 2px 8px rgba(42, 38, 34, .25);
box-shadow: 0 0 0 1px rgba(42, 38, 34, .18);
cursor: pointer;
visibility: hidden;
}
@media (hover: hover) and (pointer: fine) { .mv-photo-box:hover .mp-marker { visibility: visible; } }
.mv-photo-box.is-tagging .mp-marker,
.mv-photo-box.show-names .mp-marker,
.mp-marker.is-hot { visibility: visible; }
.mp-marker.is-hot { border-color: #fff; box-shadow: 0 0 0 1px rgba(42, 38, 34, .25), 0 0 0 4px rgba(255, 255, 255, .25); }
.mp-marker.is-active { border-color: var(--pz-a500); box-shadow: 0 0 0 3px rgba(210, 105, 74, .35), 0 2px 8px rgba(42, 38, 34, .25); }
.mp-marker:focus-visible { outline: 2px solid var(--pz-a700); outline-offset: 3px; }
.mp-marker-name {
@@ -33,10 +41,9 @@
visibility: hidden;
}
.mp-marker:hover .mp-marker-name,
.mp-marker:focus-visible .mp-marker-name,
.mp-marker.is-hot .mp-marker-name,
.mv-photo-box.is-tagging .mp-marker-name,
.mv-photo-box.show-names .mp-marker-name { visibility: visible; }
@media (hover: none) { .mp-marker-name { visibility: visible; } }
/* Photo cliquable pendant le placement d'un point */
.mv-photo-box.is-placing img { cursor: crosshair; }
+15 -5
View File
@@ -14,9 +14,9 @@ function PersonAvatar({ person }) {
: <span className="mp-avatar mp-avatar--initial" aria-hidden="true">{person?.firstName?.[0] || '?'}</span>
}
// Points posés sur la photo : un rond par visage, le prénom au survol ou au focus. En identification, le point de
// la personne choisie est mis en avant.
export function MediaPeopleMarkers({ people, getPerson, activeId = null, onOpenPerson }) {
// Ronds posés sur les visages, cachés tant qu'on ne les demande pas (survol de la photo, photo touchée, prénom survolé
// dessous : `hotId`). En identification, ils restent visibles et le rond de la personne choisie est mis en avant.
export function MediaPeopleMarkers({ people, getPerson, activeId = null, hotId = null, onOpenPerson }) {
return people.filter((tag) => tag.x != null && tag.y != null).map((tag) => {
const person = getPerson(tag.personId)
if (!person) return null
@@ -25,7 +25,7 @@ export function MediaPeopleMarkers({ people, getPerson, activeId = null, onOpenP
<button
key={tag.personId}
type="button"
className={`mp-marker${activeId === tag.personId ? ' is-active' : ''}`}
className={`mp-marker${activeId === tag.personId ? ' is-active' : ''}${hotId === tag.personId ? ' is-hot' : ''}`}
style={{ left: `${tag.x * 100}%`, top: `${tag.y * 100}%` }}
onClick={(event) => { event.stopPropagation(); onOpenPerson?.(tag.personId) }}
aria-label={t('Voir la fiche de {name}', { name })}
@@ -49,6 +49,7 @@ export function MediaPeopleList({
onAdd,
onRemove,
onOpenPerson,
onHover,
}) {
const [query, setQuery] = useState('')
const taken = useMemo(() => new Set(people.map((tag) => tag.personId)), [people])
@@ -69,7 +70,16 @@ export function MediaPeopleList({
<div className="mp-list" aria-label={t('Sur la photo')}>
<span className="mp-list-label">{t('Sur la photo')}</span>
{known.map(({ person }) => (
<button key={person.id} type="button" className="mp-chip" onClick={() => onOpenPerson?.(person.id)}>
<button
key={person.id}
type="button"
className="mp-chip"
onClick={() => onOpenPerson?.(person.id)}
onPointerEnter={() => onHover?.(person.id)}
onPointerLeave={() => onHover?.(null)}
onFocus={() => onHover?.(person.id)}
onBlur={() => onHover?.(null)}
>
<PersonAvatar person={person} />
<span>{firstNameLabel(person)}</span>
</button>
+10 -2
View File
@@ -43,6 +43,10 @@ const MediaViewer = ({
const [tagging, setTagging] = useState(false)
const [draftPeople, setDraftPeople] = useState([])
const [activeTagId, setActiveTagId] = useState(null)
// Visages identifiés : rien sur la photo par défaut (Patch, 27/10). Toucher la photo montre ou cache les prénoms,
// survoler un prénom sous la photo entoure son visage.
const [showNames, setShowNames] = useState(false)
const [hotPersonId, setHotPersonId] = useState(null)
const [savingPeople, setSavingPeople] = useState(false)
const [peopleError, setPeopleError] = useState('')
const [peopleNotice, setPeopleNotice] = useState('')
@@ -310,12 +314,16 @@ const MediaViewer = ({
// La boîte épouse l'image : les points (x, y en proportion) tombent pile sur les visages
return (
<div className="media-photo-frame">
<div className={`mv-photo-box${tagging ? ' is-tagging' : ''}${activeTagId ? ' is-placing' : ''}`} onClick={placeActiveTag}>
<div
className={`mv-photo-box${tagging ? ' is-tagging' : ''}${activeTagId ? ' is-placing' : ''}${showNames ? ' show-names' : ''}`}
onClick={(event) => (tagging ? placeActiveTag(event) : setShowNames((shown) => !shown))}
>
<img src={mediaUrl} alt={media?.label || t('Photo')} />
<MediaPeopleMarkers
people={shownPeople}
getPerson={getPerson}
activeId={activeTagId}
hotId={tagging ? null : hotPersonId}
onOpenPerson={tagging ? (id) => setActiveTagId(id) : onOpenPerson}
/>
</div>
@@ -666,7 +674,7 @@ const MediaViewer = ({
</div>
) : (
<div className="mv-people">
<MediaPeopleList people={savedPeople} getPerson={getPerson} onOpenPerson={onOpenPerson} />
<MediaPeopleList people={savedPeople} getPerson={getPerson} onOpenPerson={onOpenPerson} onHover={setHotPersonId} />
{canTag && onSavePeople && (
<button type="button" className="pz-btn pz-btn--soft pz-btn--sm mv-people-btn" onClick={startTagging}>
<UserPlus size={15} strokeWidth={2} aria-hidden="true" />