gestion_table_tournante_libre/src/interface/plan/TablePlan.svelte
Mathieu Benoit 134e7850b7 [ADD] interface: event list, open event, participants, import, plan
The working screens of an evening. The home screen lists the events of
the working folder, asks its question, loads demos, copies, renames,
imports and deletes, and shows what the trash holds. An open event frames
its mode, offers Modifier, Débloquer and a confirmed lock takeover, and
shows its warnings. Participants get a grid, a form and a file; an import
goes through a preview and a report; tables, the default and tours have
their screens; the plan draws tables, chairs, empty seats and names.
Checked: 2342 node, 356 browser, 60 node-long; the Electron start check.

--- FR ---

[ADD] interface : liste, événement, participants, import, tables, plan

Les écrans de travail d'une soirée. L'accueil liste les événements du
dossier de travail, pose sa question, charge les démonstrations, copie,
renomme, importe et supprime, et montre ce que tient la corbeille. Un
événement ouvert encadre son mode, offre Modifier, Débloquer et une
reprise de verrou confirmée, et montre ses avertissements. Les
participants ont une grille, un formulaire et une fiche ; un import passe
par un aperçu et un rapport ; tables, défaut et tours ont leurs écrans ;
le plan dessine tables, chaises, sièges vides et noms.
Vérifié : 2342 node, 356 navigateur, 60 node-long ; démarrage d'Electron.

Assisted-by: Claude Opus 5.5
2026-10-07 08:12:27 -04:00

193 lines
6 KiB
Svelte

<!-- © 2026 TechnoLibre (http://www.technolibre.ca)
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -->
<svelte:options namespace="svg" />
<script>
// Une table du plan (§ 7.1, § 7.3, § 7.5, § 7.6) : le groupe [data-table],
// qui ne porte que le plateau et les chaises, avec les marques posées sur
// eux ; puis, frères du groupe, la poignée [data-poignee] et la pastille
// [data-pastille]. Tout vient de la TableDessin du modèle (modele.js) ; le
// composant ne calcule rien.
//
// En régime table, les chaises ne se dessinent pas : l'emprise entière se
// peint au fond de la chaise et devient la seule cible, sous le plateau.
// En régime sièges, chaque chaise est un groupe [data-siege="table:siège"]
// dont seul le disque reçoit le pointeur : ses marques, comme la pastille,
// ont pointer-events: none, et la cible d'une chaise est le disque même que
// la désignation lit (designation.js).
//
// Une table en conflit, quand le plan signale, porte un contour épaissi
// (contourConflit) et une trame (trameConflit) par-dessus son plateau ; sa
// pastille porte le nombre de violations, signalée ou non (§ 7.5).
import { getContext } from 'svelte';
import { RAYON_POIGNEE } from '../../geometrie/tables.js';
import {
CADENAS_FERME,
CADENAS_OUVERT,
ECART_ANNEAU,
ECART_MISE_EN_EVIDENCE,
RAYON_PASTILLE,
TAILLE_TEXTE_PASTILLE_CM,
TRACE_GEL,
formesTable,
} from './modele.js';
let { table, signaler, prefixe } = $props();
const t = getContext('t');
// La marque que porte le disque d'une chaise, selon son remplissage.
const MARQUE_DU_REMPLISSAGE = { normal: 'contourChaise', plein: 'remplissagePlein', hachure: 'hachure' };
const TRACE_DU_CADENAS = { cadenasFerme: CADENAS_FERME, cadenasOuvert: CADENAS_OUVERT };
const formes = $derived(formesTable(table.geometrie));
const poignee = $derived(table.geometrie.poignee);
const signal = $derived(signaler && table.conflit);
const hachure = $derived(`url(#${prefixe}-hachure)`);
const trame = $derived(`url(#${prefixe}-trame)`);
</script>
<g data-table={table.id} role="group" aria-label={t(table.nomAccessible.cle, table.nomAccessible.details)}>
{#if table.regime === 'table'}
{#if formes.ronde}
<circle class="emprise" data-marque="contourChaise" {...formes.emprise} />
{:else}
<rect class="emprise" data-marque="contourChaise" {...formes.emprise} />
{/if}
{/if}
{#if formes.ronde}
<circle class="plateau" class:conflit={signal} data-marque={signal ? 'contourConflit' : 'contourTable'} {...formes.plateau} />
{:else}
<rect class="plateau" class:conflit={signal} data-marque={signal ? 'contourConflit' : 'contourTable'} {...formes.plateau} />
{/if}
{#if signal}
{#if formes.ronde}
<circle class="trame" data-marque="trameConflit" fill={trame} {...formes.plateau} />
{:else}
<rect class="trame" data-marque="trameConflit" fill={trame} {...formes.plateau} />
{/if}
{/if}
{#if table.gelee}
<path class="gel" data-marque="gel" d={TRACE_GEL} transform="translate({formes.gel.x},{formes.gel.y})" />
{/if}
{#each table.chaises as chaise (chaise.siege)}
<g data-siege="{table.id}:{chaise.siege}">
<circle
class="chaise"
class:pleine={chaise.remplissage === 'plein'}
data-marque={MARQUE_DU_REMPLISSAGE[chaise.remplissage]}
style:fill={chaise.remplissage === 'hachure' ? hachure : null}
cx={chaise.x}
cy={chaise.y}
r={chaise.rayon}
/>
{#each chaise.marques as marque (marque)}
{#if marque === 'anneau'}
<circle class="anneau" data-marque="anneau" cx={chaise.x} cy={chaise.y} r={chaise.rayon + ECART_ANNEAU} />
{:else}
<path class="cadenas" data-marque={marque} d={TRACE_DU_CADENAS[marque]} transform="translate({chaise.x},{chaise.y})" />
{/if}
{/each}
{#if chaise.miseEnEvidence}
<circle class="evidence" data-marque="miseEnEvidence" cx={chaise.x} cy={chaise.y} r={chaise.rayon + ECART_MISE_EN_EVIDENCE} />
{/if}
</g>
{/each}
</g>
<circle class="poignee" data-poignee={table.id} data-marque="poignee" cx={poignee.x} cy={poignee.y} r={RAYON_POIGNEE} />
{#if table.pastille > 0}
<g class="pastille" data-pastille={table.id}>
<circle class="disque" data-marque="pastille" cx={formes.pastille.x} cy={formes.pastille.y} r={RAYON_PASTILLE} />
<text
class="nombre"
x={formes.pastille.x}
y={formes.pastille.y}
font-size={TAILLE_TEXTE_PASTILLE_CM}
text-anchor="middle"
dominant-baseline="central">{table.pastille}</text>
</g>
{/if}
<style>
/* Les traits gardent leur épaisseur à l'écran, à tout zoom (§ 7.2). */
.emprise,
.plateau,
.chaise,
.poignee {
vector-effect: non-scaling-stroke;
stroke-width: 1.5;
}
.emprise {
fill: var(--couleur-chaise-fond);
stroke: var(--couleur-chaise-trait);
}
.plateau {
fill: var(--couleur-table-fond);
stroke: var(--couleur-table-trait);
}
.plateau.conflit {
stroke: var(--couleur-conflit);
stroke-width: 4;
}
.trame {
stroke: none;
pointer-events: none;
}
.gel {
fill: var(--couleur-gel);
pointer-events: none;
}
.chaise {
fill: var(--couleur-chaise-fond);
stroke: var(--couleur-chaise-trait);
}
.chaise.pleine {
fill: var(--couleur-chaise-pleine);
}
.anneau {
fill: none;
stroke: var(--couleur-marque);
stroke-width: 4;
pointer-events: none;
}
.cadenas {
fill: var(--couleur-marque);
pointer-events: none;
}
.evidence {
fill: none;
stroke: var(--couleur-mise-en-evidence);
stroke-width: 4;
pointer-events: none;
}
.poignee {
fill: var(--couleur-poignee);
stroke: var(--couleur-poignee);
}
.pastille {
pointer-events: none;
}
.disque {
fill: var(--couleur-pastille-fond);
}
.nombre {
fill: var(--couleur-pastille-texte);
font-family: var(--police-interface);
font-weight: 700;
}
</style>