Every gesture the pointer offers on the plan is reachable from the keyboard (§ 7.3): one stop enters the plan, arrows move between tables and seats, and the table and place panels choose a seat and keep the focus. A tooltip names who sits where; a per-person menu moves, assigns or reserves without dragging; the reserve and the work lists choose a person to designate on the plan. Escape, a tour change and the executed command forget the choice, and a click never moves the view. Checked: 2537 node, 466 browser (one expected failure kept), 60 node-long. --- FR --- [ADD] interface : clavier du plan, infobulle, panneaux, menu, listes Chaque geste que le pointeur offre sur le plan s'atteint au clavier (§ 7.3) : un seul arrêt entre dans le plan, les flèches vont de table en table et de siège en siège, et les panneaux de table et de place choisissent un siège en gardant le focus. Une infobulle nomme qui est assis où ; un menu par personne déplace, assigne ou réserve sans glisser ; la réserve et les listes de travail choisissent une personne à désigner sur le plan. Échap, un changement de tour et la commande exécutée oublient le choix, et un clic ne déplace jamais la vue. Vérifié : 2537 node, 466 navigateur (un échec attendu gardé), 60 node-long. Assisted-by: Claude Opus 5.5
74 lines
2.3 KiB
Svelte
74 lines
2.3 KiB
Svelte
<!-- © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -->
|
|
|
|
<script>
|
|
// L'infobulle du plan (§ 7.1, § 7.3) : un seul élément [role=tooltip],
|
|
// toujours présent dans la surface du plan, hors du <svg data-plan>, caché
|
|
// tant qu'aucune table en conflit n'est désignée. Plan.svelte l'ouvre par
|
|
// trois déclencheurs — le survol, le focus du clavier, la pression longue —
|
|
// et lui passe le détail des conflits (detailConflits, modele.js) et sa
|
|
// position dans la surface, en px. Elle énonce la table et son nombre de
|
|
// violations, puis chaque règle violée et ses groupes : chaque nom occupe
|
|
// son propre nœud, relié à l'autre nom d'une paire par « et », séparé du
|
|
// groupe suivant.
|
|
//
|
|
// infobulle : null, ou {detail, x, y} ; id : l'identifiant que la racine
|
|
// du plan désigne par aria-describedby quand l'infobulle s'ouvre par le
|
|
// focus.
|
|
import { getContext } from 'svelte';
|
|
|
|
let { infobulle, id } = $props();
|
|
|
|
const t = getContext('t');
|
|
</script>
|
|
|
|
<div
|
|
class="infobulle"
|
|
role="tooltip"
|
|
{id}
|
|
hidden={infobulle === null}
|
|
style:left={infobulle === null ? null : `${infobulle.x}px`}
|
|
style:top={infobulle === null ? null : `${infobulle.y}px`}
|
|
>
|
|
{#if infobulle !== null}
|
|
<p class="entete">{t('plan.infobulle.entete', { numero: infobulle.detail.numero, nombre: infobulle.detail.nombre })}</p>
|
|
{#each infobulle.detail.regles as regle, rang (rang)}
|
|
<p>
|
|
<span>{t(regle.cle)}</span>{#each regle.groupes as groupe, g (g)}{#if g > 0}<span
|
|
>{t('plan.infobulle.separateur')}</span
|
|
>{/if}{#each groupe as nom, n (n)}{#if n > 0}<span>{t('plan.infobulle.et')}</span>{/if}<span class="donnee"
|
|
>{nom}</span
|
|
>{/each}{/each}
|
|
</p>
|
|
{/each}
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.infobulle {
|
|
position: absolute;
|
|
z-index: 2;
|
|
box-sizing: border-box;
|
|
max-width: 24rem;
|
|
padding: var(--espace-2);
|
|
border: 1px solid var(--couleur-page-filet);
|
|
border-radius: var(--rayon-1);
|
|
background: var(--couleur-page-fond);
|
|
color: var(--couleur-page-texte);
|
|
box-shadow: var(--ombre-1);
|
|
font-size: var(--taille-texte);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.infobulle[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
p {
|
|
margin: 0;
|
|
}
|
|
|
|
.entete {
|
|
font-weight: 700;
|
|
}
|
|
</style>
|