The wheel no longer zooms during a gesture, so a drop lands under the ghost. A seat is a target the size of its pitch. A floor drop refuses to remove an every-tour reservation. A seat reservation the retained plan does not hold is flagged at the seat, with its swap as remedy. DERIVE names the person; capacities have an upper bound; Generate waits for a pending setting; a worker that fails to load shows a refusal; § 5.3 constraints get their switches. Commands are not re-entrant and a double click does not confirm. Checked: 2755 node, 550 browser tests, from the index alone. --- FR --- [FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme. Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse de retirer une réservation de tous les tours. Une réservation à un siège que le retenu ne tient pas est relevée au siège, l'échange en remède. La dérive nomme la personne ; les capacités ont une borne ; Générer attend un réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le § 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul. Assisted-by: Claude Opus 5.5
122 lines
4.4 KiB
Svelte
122 lines
4.4 KiB
Svelte
<!-- © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -->
|
|
|
|
<script>
|
|
// La barre de vue du plan (§ 7.2, § 7.7, § 8.4) : le sélecteur de tour,
|
|
// « Zoom + », « Zoom moins », « Ajuster », l'affichage des listes et le plein
|
|
// écran. Des contrôles HTML, hors du <svg data-plan>, tous vivants en
|
|
// lecture comme sur un plan bloqué : aucun n'écrit l'événement. Le tour,
|
|
// les listes et le plein écran passent par l'état de navigation ('ui') ;
|
|
// les zooms et l'ajustement par Plan.svelte, qui tient la vue :
|
|
// zoomer(sens), sens valant 1 ou -1, et ajuster(). Un bouton à bascule dit
|
|
// son état par aria-pressed, que la forme du bouton redit.
|
|
//
|
|
// evidence : la personne mise en évidence, {id, nom} — nom null pour une
|
|
// personne que la charge n'a plus —, ou null. La barre la nomme et offre
|
|
// d'ôter la mise en évidence, que la personne soit assise au tour montré
|
|
// ou non ; le menu de la personne la pose (MenuPersonne). Une personne que
|
|
// la charge n'a plus se dit telle, sans nom.
|
|
//
|
|
// source : ce que la barre dit du placement montré, que rend sourceMontree
|
|
// (vue_plan.js) — {libelle, revenir} — : la barre le nomme, [data-source],
|
|
// et, quand une proposition est demandée, offre de ne plus la montrer
|
|
// (ui.montrerProposition(null)), sous le libellé revenir. Ni l'un ni
|
|
// l'autre n'écrit l'événement.
|
|
import { getContext } from 'svelte';
|
|
import { toursOfferts } from './vue_plan.js';
|
|
|
|
let { tour, tours, zoomer, ajuster, evidence = null, source } = $props();
|
|
|
|
const t = getContext('t');
|
|
const ui = getContext('ui');
|
|
|
|
const offerts = $derived(toursOfferts(tours));
|
|
</script>
|
|
|
|
<div class="barre-vue" role="toolbar" aria-label={t('plan.vue.barre')}>
|
|
<p class="source" data-source>{t(source.libelle.cle, source.libelle.details)}</p>
|
|
{#if source.revenir !== null}
|
|
<button type="button" data-commande="quitterProposition" onclick={() => ui.montrerProposition(null)}
|
|
>{t(source.revenir)}</button
|
|
>
|
|
{/if}
|
|
<label class="tour">
|
|
<span>{t('plan.vue.tour')}</span>
|
|
<select data-champ="tour" value={tour} onchange={(evenement) => ui.montrerTour(Number(evenement.currentTarget.value))}>
|
|
{#each offerts as numero (numero)}
|
|
<option value={numero}>{numero}</option>
|
|
{/each}
|
|
</select>
|
|
</label>
|
|
<button type="button" data-commande="zoomer" onclick={() => zoomer(1)}>{t('plan.vue.zoomer')}</button>
|
|
<button type="button" data-commande="dezoomer" onclick={() => zoomer(-1)}>{t('plan.vue.dezoomer')}</button>
|
|
<button type="button" data-commande="ajuster" onclick={() => ajuster()}>{t('plan.vue.ajuster')}</button>
|
|
<button type="button" data-commande="listes" aria-pressed={$ui.listes} onclick={() => ui.basculerListes()}
|
|
>{t('plan.vue.listes')}</button
|
|
>
|
|
<button type="button" data-commande="pleinEcran" aria-pressed={$ui.pleinEcran} onclick={() => ui.basculerPleinEcran()}
|
|
>{t('plan.vue.pleinEcran')}</button
|
|
>
|
|
{#if evidence !== null}
|
|
<p class="evidence" data-evidence={evidence.id}>
|
|
<span>{t('plan.evidence.personne')}</span>{#if evidence.nom !== null}<span>{evidence.nom}</span>{:else}<span
|
|
>{t('plan.evidence.absente')}</span
|
|
>{/if}
|
|
<button type="button" data-commande="oterEvidence" onclick={() => ui.mettreEnEvidence(null)}>{t('plan.evidence.oter')}</button>
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.barre-vue {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: var(--espace-2);
|
|
}
|
|
|
|
.source {
|
|
margin: 0;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.evidence {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--espace-1);
|
|
margin: 0;
|
|
}
|
|
|
|
.tour {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--espace-1);
|
|
}
|
|
|
|
button,
|
|
select {
|
|
padding: var(--espace-1) var(--espace-3);
|
|
border: 1px solid var(--couleur-page-filet);
|
|
border-radius: var(--rayon-1);
|
|
background-color: var(--couleur-page-fond);
|
|
color: var(--couleur-page-texte);
|
|
font: inherit;
|
|
}
|
|
|
|
button {
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* Une bascule active se distingue par sa forme : trait intérieur épais,
|
|
texte souligné ; sa boîte ne change pas, et la barre ne bouge pas. */
|
|
button[aria-pressed='true'] {
|
|
box-shadow: inset 0 0 0 2px var(--couleur-page-texte);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
button:focus-visible,
|
|
select:focus-visible {
|
|
outline: 2px solid var(--couleur-focus);
|
|
outline-offset: 2px;
|
|
}
|
|
</style>
|