gestion_table_tournante_libre/src/interface/evenement/CadreMode.svelte

211 lines
7 KiB
Svelte
Raw Normal View History

<!-- © 2026 TechnoLibre (http://www.technolibre.ca)
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -->
<script>
// Le cadre de mode (§ 8.4, § 8.5, § 14.5) : un trait autour de la surface
// de travail, dont la forme dit le mode — tirets en lecture, plein en
// écriture, double sur un plan bloqué —, et une barre qui porte, de gauche
// à droite, la place fixe, le libellé de mode, puis « Défaire » et
// « Refaire » (§ 8.3). Sous la barre vient la tête, tete, quand elle est
// donnée — l'en-tête de l'événement et la navigation des sections —, puis
// la surface, children, qui défile seule. La barre ouvre le cadre : rien
// ne la précède dans l'écran de l'événement, si bien qu'un refus posé dans
// la tête, ou un nom qui s'étend sur plusieurs lignes, allonge la tête
// sans déplacer la place fixe (§ 8.4).
//
// La place fixe porte la commande que rend commandeFixe (modele.js) :
// « Modifier », « Débloquer » — confirmé en place, par la Commande d'I2 —,
// « Revenir en lecture », ou, pour un format plus récent, l'explication du
// refus courant à la place de toute commande. Le verrou d'une autre séance
// qui a refusé le passage en écriture s'y nomme à côté du refus — sa
// séance et l'instant de sa prise, chacun dans son nœud — et, quand sa vie
// ne se prouve pas, « Reprendre le verrou » s'y offre, confirmé en place à
// côté de ce qui le nomme (§ 8.8). « Refaire » n'est là que lorsqu'un
// retour se refait ; « Défaire » est toujours là, et la séance refuse à
// côté de lui ce qui ne se défait pas.
import { getContext } from 'svelte';
import Commande from '../primitives/Commande.svelte';
import { CONFIRMATION_DEBLOCAGE } from '../primitives/commande.js';
import Refus from '../primitives/Refus.svelte';
import { cadreDeMode, commandeFixe, repriseOfferte } from './modele.js';
let { children, tete = null } = $props();
const t = getContext('t');
const seance = getContext('seance');
// Sans événement — l'instant où l'écran se retire —, le cadre garde la
// forme de la lecture, et n'a pas de libellé.
const cadre = $derived(cadreDeMode($seance));
const forme = $derived(cadre?.forme ?? 'tirets');
const commande = $derived(commandeFixe($seance));
const verrou = $derived($seance.mode === 'lecture' ? $seance.verrouAutre : null);
const reprise = $derived(repriseOfferte($seance));
</script>
<div class="cadre" data-mode={forme} class:tirets={forme === 'tirets'} class:double={forme === 'double'}>
<div class="barre" role="group" aria-label={t('evenement.cadre.commandes')}>
<div class="ligne">
<div class="fixe" data-place-fixe>
{#if commande === 'format'}
<Refus refus={$seance.refus} />
{:else if commande === 'modifier'}
<Commande cle="communs.commande.modifier" nom="modifier" executer={() => seance.passerEnEcriture()} />
{:else if commande === 'debloquer'}
<Commande
cle="communs.commande.debloquer"
nom="debloquer"
confirmer={CONFIRMATION_DEBLOCAGE}
executer={() => seance.debloquer()}
/>
{:else if commande === 'lecture'}
<Commande cle="communs.commande.lecture" nom="lecture" executer={() => seance.passerEnLecture()} />
{/if}
</div>
{#if cadre !== null}
<span class="libelle" data-mode-libelle="cadre">{t(cadre.cle)}</span>
{/if}
{#if commande !== 'format'}
<div class="historique" data-historique>
<Commande cle="evenement.commande.defaire" nom="defaire" executer={() => seance.defaire()} />
{#if $seance.retours.refaire}
<Commande cle="evenement.commande.refaire" nom="refaire" executer={() => seance.refaire()} />
{/if}
</div>
{/if}
</div>
{#if verrou !== null}
<div class="verrou" data-verrou>
<p>{t('evenement.verrou.titre')}</p>
<p>
<span>{t('evenement.verrou.seance')}</span>
<span class="donnee" data-verrou-seance>{verrou.seance ?? t('evenement.verrou.inconnu')}</span>
<span>{t('evenement.verrou.depuis')}</span>
<span class="donnee" data-verrou-depuis>{verrou.depuis ?? t('evenement.verrou.inconnu')}</span>
</p>
{#if reprise !== null}
<p>{t('evenement.verrou.avis')}</p>
<Commande
cle="communs.remede.reprendreVerrou"
nom="reprendreVerrou"
confirmer="evenement.verrou.confirmerReprise"
executer={() => seance.reprendreVerrou()}
/>
{/if}
</div>
{/if}
</div>
{#if tete !== null}
<div class="tete">
{@render tete()}
</div>
{/if}
<div class="surface">
{@render children()}
</div>
</div>
<style>
/* Le trait du cadre : sa forme dit le mode, sa couleur le redit. */
/* Une même épaisseur dans les trois formes : la place fixe ne bouge pas
d'un mode à l'autre. */
.cadre {
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: 0;
border: 6px solid var(--couleur-cadre-ecriture);
border-radius: var(--rayon-2);
}
.cadre.tirets {
border-style: dashed;
border-color: var(--couleur-cadre-lecture);
}
.cadre.double {
border-style: double;
border-color: var(--couleur-cadre-bloque);
}
/* La barre ne prend jamais toute la hauteur du cadre : au-delà, ses
messages défilent, la place fixe en tête. */
.barre {
display: flex;
flex: 0 0 auto;
flex-direction: column;
gap: var(--espace-2);
max-height: 50%;
overflow-y: auto;
padding: var(--espace-2) var(--espace-3);
border-bottom: 1px solid var(--couleur-page-filet);
}
.ligne {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: var(--espace-2) var(--espace-3);
}
.fixe {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: var(--espace-2);
}
.libelle {
padding: var(--espace-1) var(--espace-2);
border: 1px solid var(--couleur-page-filet);
border-radius: var(--rayon-1);
font-weight: bold;
}
.historique {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: var(--espace-2);
}
.verrou {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--espace-1);
font-size: var(--taille-texte);
}
.verrou p {
display: flex;
flex-wrap: wrap;
gap: var(--espace-1) var(--espace-2);
margin: 0;
}
.donnee {
font-weight: bold;
}
/* La tête ne prend jamais plus du tiers du cadre : au-delà, elle défile,
et la surface garde sa place. */
.tete {
display: flex;
flex: 0 0 auto;
flex-direction: column;
gap: var(--espace-2);
max-height: 35%;
overflow-y: auto;
padding: var(--espace-2) var(--espace-3);
border-bottom: 1px solid var(--couleur-page-filet);
}
.surface {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: var(--espace-3);
}
</style>