gestion_table_tournante_libre/src/interface/generation/Reglages.svelte

129 lines
4.3 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>
// Les réglages d'une génération (§ 5.7, § 5.10) : la graine, puis le
// nombre de propositions, le compte d'arrêt et l'historique, chacun sous
// son libellé, qui porte son unité ; puis la durée observée du dernier
// calcul de la séance, et ce qu'elle laisse attendre des réglages courants.
// Aucun champ ne prend une durée : la recherche s'arrête sur un compte.
//
// Les trois réglages de la charge (generation.nombre, generation.arret,
// generation.historique) sont des Champ : une saisie se lit par la
// validation du contrôleur de génération, la même que celle de
// « Générer », et s'écrit à la sortie du champ par modifierReglage — une
// entrée par changement ; en lecture, la séance refuse la première frappe.
//
// La graine n'est pas au fichier : c'est une saisie de la commande, vivante
// en lecture, que le parent tient (graine, liée) et contrôle au clic de
// « Générer ». Son refus, refusGraine, se pose contre elle ; une frappe
// appelle saisirGraine, qui le retire.
import { getContext } from 'svelte';
import Champ from '../primitives/Champ.svelte';
import Refus from '../primitives/Refus.svelte';
let { charge, mesure, estimation, graine = $bindable(), refusGraine = null, saisirGraine } = $props();
const t = getContext('t');
const seance = getContext('seance');
const generation = getContext('generation');
const uid = $props.id();
const idGraine = `${uid}-graine`;
const idRefusGraine = `${uid}-graine-refus`;
// Les réglages de la charge, dans l'ordre de l'écran.
const REGLAGES = ['nombre', 'arret', 'historique'];
// La lecture d'une saisie du réglage nom : la validation du contrôleur sur
// les réglages proposés, ce réglage remplacé par la saisie ; son refus, ou
// à défaut le premier, sinon la valeur lue.
const lecteur = (nom) => (texte) => {
const { reglages, refus } = generation.valider({ ...generation.reglagesProposes(), [nom]: texte });
const propre = refus.find(({ champ }) => champ === nom) ?? refus[0];
if (propre !== undefined) return { refus: { code: propre.code, details: propre.details } };
return { valeur: reglages[nom] };
};
const ecrire = (nom) => (valeur) => seance.executer('modifierReglage', { cle: `generation.${nom}`, valeur });
</script>
<div class="reglages" data-reglages>
<div class="champs">
<div class="champ">
<label for={idGraine}>{t('generation.reglage.graine')}</label>
<input
id={idGraine}
type="text"
data-champ="graine"
bind:value={graine}
aria-invalid={refusGraine === null ? undefined : 'true'}
aria-describedby={refusGraine === null ? undefined : idRefusGraine}
oninput={() => saisirGraine()}
/>
{#if refusGraine !== null}
<Refus id={idRefusGraine} refus={refusGraine} />
{/if}
</div>
{#each REGLAGES as nom (nom)}
<Champ
cle={`generation.${nom}`}
etiquette={`generation.reglage.${nom}`}
valeur={charge.reglages.generation[nom]}
lire={lecteur(nom)}
executer={ecrire(nom)}
/>
{/each}
</div>
{#if mesure === null}
<p data-duree>{t('generation.duree.inconnue')}</p>
{:else}
<p data-duree>{t('generation.duree.observee', mesure)}</p>
<p data-estimation>{t('generation.duree.estimee', { secondes: estimation.secondes })}</p>
{/if}
</div>
<style>
.reglages {
display: flex;
flex-direction: column;
gap: var(--espace-3);
}
.champs {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: var(--espace-4);
}
.champ {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--espace-1);
}
input {
padding: var(--espace-1) var(--espace-2);
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;
font-size: var(--taille-texte);
}
input:focus-visible {
outline: 2px solid var(--couleur-focus);
outline-offset: 1px;
}
input[aria-invalid='true'] {
border-color: var(--couleur-refus);
}
p {
margin: 0;
}
</style>