[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
<!-- © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -->
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
// Le plan de salle et sa vue (§ 7.2, § 7.3, § 7.7, § 8.4, § 8.5) : la
|
|
|
|
|
// barre de vue, puis la surface [data-surface] qui porte le bandeau du
|
|
|
|
|
// plan, le <svg data-plan> et l'échelle graphique. Le <svg> racine n'a ni
|
|
|
|
|
// viewBox, ni bordure, ni remplissage : son unité est le pixel CSS, et le
|
|
|
|
|
// dessin vit dans un seul groupe <g data-vue>, dont l'attribut transform
|
|
|
|
|
// est la vue, {k, tx, ty}. Un zoom n'écrit que cet attribut sous la
|
|
|
|
|
// racine ; l'échelle et le bandeau, hors du <svg>, gardent leur taille à
|
|
|
|
|
// l'écran.
|
|
|
|
|
//
|
|
|
|
|
// Le composant est le seul à calculer le modèle de dessin (modele.js), de
|
|
|
|
|
// l'état des places du tour montré, au k de sa vue. La vue est null tant
|
|
|
|
|
// qu'aucun cadre ne la permet — plan monté caché, cadre où kAjuste vaut
|
|
|
|
|
// null — : rien ne se dessine alors, et le groupe ne porte pas de
|
|
|
|
|
// transform. Un ResizeObserver sur la racine relit le cadre à chaque
|
|
|
|
|
// changement de taille : tant que la vue est null, il la calcule par
|
|
|
|
|
// vueInitiale, du cadrage que la séance a mémorisé ; ensuite par
|
|
|
|
|
// vueApresCadre. Ni l'une ni l'autre ne s'écrit : seul un geste de
|
|
|
|
|
// l'opérateur — molette, « Zoom + », « Zoom moins », « Ajuster » — confie sa
|
|
|
|
|
// vue à seance.demanderCadrage, qui l'écrit, différée, dans
|
|
|
|
|
// reglages_locaux.json, jamais dans l'événement.
|
|
|
|
|
//
|
|
|
|
|
// Le cadre se relit au moment du geste, par getBoundingClientRect de la
|
|
|
|
|
// racine : un défilement de la page le déplace sans changer sa taille.
|
|
|
|
|
// Les écouteurs se posent sur la racine par addEventListener — Svelte 5
|
|
|
|
|
// délègue au conteneur de montage un gestionnaire déclaré dans le
|
|
|
|
|
// gabarit —, celui de la molette non passif, pour que la page ne défile
|
|
|
|
|
// pas sous un zoom. touch-action: none sur la racine et la surface : le
|
|
|
|
|
// navigateur ne prend aucun glissement du doigt pour un défilement.
|
|
|
|
|
//
|
|
|
|
|
// diagnostic : celui que rend diagnostiquer, que SectionPlan passe, ou
|
|
|
|
|
// null — le bandeau ne porte alors que la phrase des conflits (§ 7.5).
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
// etatTour : l'état des places du tour affiché (EtatPlaces, A5), que
|
|
|
|
|
// SectionPlan calcule pour le plan et les panneaux.
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
//
|
|
|
|
|
// Les gestes du pointeur (§ 7.1, § 7.3, § 8.4) passent par un seul
|
|
|
|
|
// écouteur de chaque type, sur la racine : pointerdown identifie la source
|
|
|
|
|
// — poignée, chaise, table, sinon le sol — par closest, relit le cadre, et
|
|
|
|
|
// la machine de gestes.js capture le pointeur sur la racine ; les
|
|
|
|
|
// mouvements se regroupent par image (requestAnimationFrame) et ne lisent
|
|
|
|
|
// aucune mise en page ; pointerup achève, par au plus une commande de la
|
|
|
|
|
// séance ; pointercancel, Échap — écouté sur document, en phase de
|
|
|
|
|
// capture, le temps du geste — et le passage en lecture (surAnnulation)
|
|
|
|
|
// restaurent. Pendant le glissement, le composant écrit l'attribut
|
|
|
|
|
// transform directement — groupe de la vue, groupe d'une table et ses
|
|
|
|
|
// frères, fantôme — et ne confie le résultat au modèle qu'au relâcher :
|
|
|
|
|
// aucun élément capturant n'est remplacé. La poignée passe son nombre au
|
|
|
|
|
// modèle de dessin (option poignee), sans rien écrire. Un refus — du
|
|
|
|
|
// dépôt, du contrôle de la poignée, de la commande — se pose sur la place
|
|
|
|
|
// par RefusPlace, hors du <svg>, et la place le désigne par
|
|
|
|
|
// aria-describedby ; l'appui suivant le retire. Tant que la commande d'un
|
|
|
|
|
// geste n'est pas réglée, un appui ne saisit que le sol.
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
//
|
|
|
|
|
// La pose — refus ou avertissements sur une place — vit dans l'état de
|
|
|
|
|
// navigation (ui.pose) : un panneau latéral y pose le refus d'une commande
|
|
|
|
|
// de place, que le plan montre comme celui d'un geste. Son remède
|
|
|
|
|
// choisirPlace (PLACE_RESERVEE) fait de la réservation qui tient la place
|
|
|
|
|
// le choix en attente (ui.choix). Une commande d'un geste exécutée oublie
|
|
|
|
|
// le choix en attente.
|
|
|
|
|
//
|
|
|
|
|
// Le clavier (§ 7.3, § 13.5) : la racine est le seul arrêt de tabulation
|
|
|
|
|
// du plan — tabindex 0, un role, un aria-label — et désigne l'élément
|
|
|
|
|
// actif, une table ou un siège, par aria-activedescendant, sur les id que
|
|
|
|
|
// TablePlan pose. Les touches passent par toucheDuPlan (clavier.js) :
|
|
|
|
|
// flèches, entrée, échappement. L'entrée sur un siège le sélectionne et
|
|
|
|
|
// porte le focus au panneau de la place ; en régime table, elle ouvre le
|
|
|
|
|
// panneau de la table et porte le focus sur la liste de ses sièges. Le
|
|
|
|
|
// panneau rend le focus à la racine par Échap. Échap sur la racine oublie
|
|
|
|
|
// le choix en attente, quel que soit l'élément actif. L'élément actif suit
|
|
|
|
|
// la place sélectionnée. Une flèche, un focus venu du clavier et une
|
|
|
|
|
// sélection venue d'ailleurs que du pointeur — un panneau, une liste de
|
|
|
|
|
// travail — amènent l'élément actif dans le cadre (vueQuiMontre) ; la
|
|
|
|
|
// sélection d'un appui sur le plan ne déplace pas la vue : la chaise
|
|
|
|
|
// cliquée reste sous le pointeur.
|
|
|
|
|
// La marque focus ne se montre que sous un focus venu du clavier
|
|
|
|
|
// (:focus-visible, puis toute touche) : un clic n'en pose pas.
|
|
|
|
|
//
|
|
|
|
|
// L'infobulle (§ 7.1, § 7.3) : un seul [role=tooltip], ouvert sur une table
|
|
|
|
|
// en conflit par le survol — un pointermove de la racine sans bouton
|
|
|
|
|
// enfoncé, hors d'un geste —, par le focus du clavier sur la table ou l'un
|
|
|
|
|
// de ses sièges, et par la pression longue d'un doigt, minuterie de
|
|
|
|
|
// l'horloge de DUREE_PRESSION_LONGUE_MS, annulée au relâcher, à
|
|
|
|
|
// l'annulation, ou quand la machine du geste juge l'appui devenu
|
|
|
|
|
// glissement, au-delà du seuil. Elle reste ouverte jusqu'à l'appui
|
|
|
|
|
// suivant.
|
|
|
|
|
import { getContext, tick, untrack } from 'svelte';
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
import { controlerCapacite } from '../../application/capacite.js';
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
import { constante } from '../../geometrie/constantes.js';
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
import { ajuster as vueAjustee, ecranDepuisDessin, transformDe } from '../../geometrie/vue.js';
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
import BandeauPlan from './BandeauPlan.svelte';
|
|
|
|
|
import BarreVue from './BarreVue.svelte';
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
import { toucheDuPlan, vueQuiMontre } from './clavier.js';
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
import { deposer } from './depot.js';
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
import EchellePlan from './EchellePlan.svelte';
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
import Fantome from './Fantome.svelte';
|
|
|
|
|
import { REPOS, cleFantome, geste, memoriser } from './gestes.js';
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
import Infobulle from './Infobulle.svelte';
|
|
|
|
|
import { detailConflits, modeleDessin } from './modele.js';
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
import PlanDessin from './PlanDessin.svelte';
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
import RefusPlace from './RefusPlace.svelte';
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
import {
|
|
|
|
|
cadreDepuisRect,
|
|
|
|
|
englobantDuTour,
|
|
|
|
|
nomDeCharge,
|
|
|
|
|
vueApresCadre,
|
|
|
|
|
vueInitiale,
|
|
|
|
|
zoomerBouton,
|
|
|
|
|
zoomerMolette,
|
|
|
|
|
} from './vue_plan.js';
|
|
|
|
|
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
let { diagnostic = null, etatTour } = $props();
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
|
|
|
|
|
const t = getContext('t');
|
|
|
|
|
const seance = getContext('seance');
|
|
|
|
|
const ui = getContext('ui');
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
const horloge = getContext('horloge');
|
|
|
|
|
|
|
|
|
|
const DUREE_PRESSION = constante('DUREE_PRESSION_LONGUE_MS');
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
|
|
|
|
|
let racine = $state(null);
|
|
|
|
|
let vue = $state.raw(null);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
let surface = $state(null);
|
|
|
|
|
let groupeVue = $state(null);
|
|
|
|
|
let elementFantome = $state(null);
|
|
|
|
|
// Le nombre que la poignée tirée montre, {table, places}, ou null.
|
|
|
|
|
let poignee = $state.raw(null);
|
|
|
|
|
// Le fantôme montré, {nom, cle}, ou null.
|
|
|
|
|
let fantome = $state.raw(null);
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// Le coin du <svg> dans la surface, en cadre dont le bord gauche et le
|
|
|
|
|
// haut sont relatifs à la surface : relu à chaque changement de cadre et à
|
|
|
|
|
// chaque appui, jamais dans un pointermove.
|
|
|
|
|
let origineSvg = $state.raw({ gauche: 0, haut: 0, largeur: 0, hauteur: 0 });
|
|
|
|
|
// L'élément actif du clavier, {table, siege|null}, ou null ; la racine a
|
|
|
|
|
// le focus ; le focus vient du clavier.
|
|
|
|
|
let actif = $state.raw(null);
|
|
|
|
|
let aLeFocus = $state(false);
|
|
|
|
|
let auClavier = $state(false);
|
|
|
|
|
// Les tables que désignent le survol et la pression longue, ou null.
|
|
|
|
|
let survol = $state(null);
|
|
|
|
|
let pression = $state(null);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
|
|
|
|
|
const prefixe = $props.id();
|
|
|
|
|
const idPose = `${prefixe}-pose`;
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
const idInfobulle = `${prefixe}-infobulle`;
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
|
|
|
|
|
const charge = $derived($seance.charge);
|
|
|
|
|
const tours = $derived(charge.evenement.tours);
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
const etat = $derived(etatTour);
|
|
|
|
|
const tour = $derived(etat.tour);
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
const englobantPlan = $derived(englobantDuTour(etat));
|
|
|
|
|
const nomDe = $derived(nomDeCharge(charge));
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
const selection = $derived($ui.selection);
|
|
|
|
|
const pose = $derived($ui.pose);
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
// La personne mise en évidence, que la barre de vue nomme : {id, nom}, nom
|
|
|
|
|
// null pour une personne que la charge n'a plus ; null sans mise en évidence.
|
|
|
|
|
const evidence = $derived($ui.miseEnEvidence === null ? null : { id: $ui.miseEnEvidence, nom: nomDe($ui.miseEnEvidence) ?? null });
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// L'élément actif que le dessin marque : sous un focus venu du clavier.
|
|
|
|
|
const actifMontre = $derived(aLeFocus && auClavier ? actif : null);
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
const modele = $derived(
|
|
|
|
|
vue === null
|
|
|
|
|
? null
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
: modeleDessin(etat, {
|
|
|
|
|
k: vue.k,
|
|
|
|
|
listes: $ui.listes,
|
|
|
|
|
miseEnEvidence: $ui.miseEnEvidence,
|
|
|
|
|
nomDe,
|
|
|
|
|
diagnostic,
|
|
|
|
|
poignee,
|
|
|
|
|
selection,
|
|
|
|
|
actif: actifMontre,
|
|
|
|
|
}),
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
);
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// La pose à sa position dans la surface, sous la vue courante ; rien pour
|
|
|
|
|
// une place dont la table n'est plus dessinée.
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
const poseAffichee = $derived.by(() => {
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
if (pose === null || vue === null || modele === null) return null;
|
|
|
|
|
const { place, refus, avertissements } = pose;
|
|
|
|
|
const ancre = ancreDe(place);
|
|
|
|
|
if (ancre === null) return null;
|
|
|
|
|
const { x, y } = ecranDepuisDessin(vue, origineSvg, ancre.x, ancre.y);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
const numero = etat.tables.find(({ id }) => id === place.table)?.numero ?? null;
|
|
|
|
|
return { id: idPose, numero, siege: place.siege ?? null, refus, avertissements, x, y };
|
|
|
|
|
});
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// L'infobulle : la table que désignent la pression longue, le survol, puis
|
|
|
|
|
// l'élément actif sous un focus venu du clavier ; rien quand elle n'a pas
|
|
|
|
|
// de conflit. Posée au coin haut droit de l'emprise.
|
|
|
|
|
const infobulleTable = $derived(pression ?? survol ?? (actifMontre === null ? null : actifMontre.table));
|
|
|
|
|
const infobulle = $derived.by(() => {
|
|
|
|
|
if (infobulleTable === null || vue === null || modele === null) return null;
|
|
|
|
|
const etatTable = etat.tables.find(({ id }) => id === infobulleTable);
|
|
|
|
|
const dessin = modele.tables.find(({ id }) => id === infobulleTable);
|
|
|
|
|
if (etatTable === undefined || dessin === undefined) return null;
|
|
|
|
|
const detail = detailConflits(etatTable, nomDe);
|
|
|
|
|
if (detail === null) return null;
|
|
|
|
|
const { centre, rayonEmprise } = dessin.geometrie;
|
|
|
|
|
const { x, y } = ecranDepuisDessin(vue, origineSvg, centre.x + rayonEmprise, centre.y - rayonEmprise);
|
|
|
|
|
return { detail, x, y };
|
|
|
|
|
});
|
|
|
|
|
const infobulleDuFocus = $derived(infobulle !== null && pression === null && survol === null);
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
|
|
|
|
|
// Le cadre de la racine, relu maintenant.
|
|
|
|
|
const cadreCourant = () => cadreDepuisRect(racine.getBoundingClientRect());
|
|
|
|
|
|
|
|
|
|
// Pose la vue d'un geste de l'opérateur et la confie à la séance ; une
|
|
|
|
|
// vue null — rien à zoomer dans ce cadre — ne change rien.
|
|
|
|
|
function poserVue(suivante) {
|
|
|
|
|
if (suivante === null) return;
|
|
|
|
|
vue = suivante;
|
|
|
|
|
seance.demanderCadrage(suivante);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function zoomer(sens) {
|
|
|
|
|
poserVue(zoomerBouton(vue, englobantPlan, cadreCourant(), sens));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ajuster() {
|
|
|
|
|
poserVue(vueAjustee(englobantPlan, cadreCourant()));
|
|
|
|
|
}
|
|
|
|
|
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
// --- Les gestes du pointeur ---------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// L'état d'une table du tour montré.
|
|
|
|
|
const tableDeEtat = (id) => etat.tables.find((table) => table.id === id);
|
|
|
|
|
|
|
|
|
|
// La source d'un appui, lue sur l'élément pressé et sur l'état des places.
|
|
|
|
|
function sourceDe(element) {
|
|
|
|
|
const saisie = element.closest('[data-poignee]');
|
|
|
|
|
if (saisie !== null) {
|
|
|
|
|
const table = Number(saisie.dataset.poignee);
|
|
|
|
|
return { genre: 'poignee', table, siege: null, participant: null, places: tableDeEtat(table).capacite };
|
|
|
|
|
}
|
|
|
|
|
const chaise = element.closest('[data-siege]');
|
|
|
|
|
if (chaise !== null) {
|
|
|
|
|
const [table, siege] = chaise.dataset.siege.split(':').map(Number);
|
|
|
|
|
const participant = tableDeEtat(table).chaises[siege - 1]?.occupant ?? null;
|
|
|
|
|
return { genre: 'personne', table, siege, participant };
|
|
|
|
|
}
|
|
|
|
|
const groupe = element.closest('[data-table]');
|
|
|
|
|
if (groupe !== null) {
|
|
|
|
|
const table = Number(groupe.dataset.table);
|
|
|
|
|
const { x, y } = tableDeEtat(table).position;
|
|
|
|
|
return { genre: 'table', table, siege: null, participant: null, position: { x, y } };
|
|
|
|
|
}
|
|
|
|
|
return { genre: 'sol', table: null, siege: null, participant: null };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Les éléments qu'un glissement de table déplace : son groupe, et ses
|
|
|
|
|
// frères désignés par son identifiant.
|
|
|
|
|
const elementsDeTable = (table) =>
|
|
|
|
|
racine.querySelectorAll(`[data-table="${table}"], [data-poignee="${table}"], [data-pastille="${table}"], [data-liste="${table}"]`);
|
|
|
|
|
|
|
|
|
|
// L'élément d'une place : la chaise, ou la table quand la place n'a pas
|
|
|
|
|
// de siège ou que sa chaise n'est pas dessinée.
|
|
|
|
|
function elementDePlace({ table, siege = null }) {
|
|
|
|
|
const chaise = siege === null ? null : racine.querySelector(`[data-siege="${table}:${siege}"]`);
|
|
|
|
|
return chaise ?? racine.querySelector(`[data-table="${table}"]`);
|
|
|
|
|
}
|
|
|
|
|
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// Le point de dessin d'une place : le centre de sa chaise, ou de sa
|
|
|
|
|
// table ; null pour une table que le dessin n'a pas.
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
function ancreDe({ table, siege = null }) {
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
const dessin = modele.tables.find(({ id }) => id === table);
|
|
|
|
|
if (dessin === undefined) return null;
|
|
|
|
|
const { geometrie } = dessin;
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
const chaise = siege === null ? undefined : geometrie.chaises[siege - 1];
|
|
|
|
|
return chaise === undefined ? { ...geometrie.centre } : { x: chaise.x, y: chaise.y };
|
|
|
|
|
}
|
|
|
|
|
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// Relit le coin du <svg> dans la surface, du cadre relu à l'instant.
|
|
|
|
|
function relireOrigine(cadre) {
|
|
|
|
|
const rect = surface.getBoundingClientRect();
|
|
|
|
|
origineSvg = { gauche: cadre.gauche - rect.left, haut: cadre.haut - rect.top, largeur: cadre.largeur, hauteur: cadre.hauteur };
|
|
|
|
|
}
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
|
|
|
|
|
// Pose un refus et des avertissements sur une place ; rien des deux
|
|
|
|
|
// retire la pose.
|
|
|
|
|
function poserSurPlace(place, refus, avertissements) {
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
ui.poserSurPlace(refus === null && avertissements.length === 0 ? null : { place, refus, avertissements });
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fermerPose = () => {
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
ui.poserSurPlace(null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Les gestes qu'un remède posé sur une place emprunte au plan : choisir
|
|
|
|
|
// une place pour la réservation qui tient la place refusée (PLACE_RESERVEE,
|
|
|
|
|
// { geste: 'choisirPlace', puis }) — elle devient le choix en attente, et
|
|
|
|
|
// « Déplacer la réservation ici » la déplace (PanneauPlace).
|
|
|
|
|
const gestesPose = {
|
|
|
|
|
choisirPlace: (remede) => {
|
|
|
|
|
const place = ui.etat().pose?.place ?? null;
|
|
|
|
|
const { participant, de } = remede.puis.arguments;
|
|
|
|
|
ui.choisir({
|
|
|
|
|
genre: 'reservation',
|
|
|
|
|
participant,
|
|
|
|
|
depuis: place === null ? null : { table: place.table, siege: place.siege ?? null },
|
|
|
|
|
de,
|
|
|
|
|
});
|
|
|
|
|
return true;
|
|
|
|
|
},
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
};
|
|
|
|
|
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// Amène une place dans le cadre, k inchangé ; la vue ne change que si la
|
|
|
|
|
// place en sort.
|
|
|
|
|
function montrer(place) {
|
|
|
|
|
if (place === null || vue === null || modele === null) return;
|
|
|
|
|
const ancre = ancreDe(place);
|
|
|
|
|
if (ancre === null) return;
|
|
|
|
|
const suivante = vueQuiMontre(vue, cadreCourant(), ancre);
|
|
|
|
|
if (suivante !== vue) poserVue(suivante);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Porte le focus à un panneau latéral de la section : à son élément
|
|
|
|
|
// [data-focus-initial], sinon au panneau.
|
|
|
|
|
async function focaliserPanneau(genre) {
|
|
|
|
|
await tick();
|
|
|
|
|
const section = racine.closest('[data-section]') ?? document;
|
|
|
|
|
const panneau = section.querySelector(`[data-panneau="${genre}"]`);
|
|
|
|
|
if (panneau === null) return;
|
|
|
|
|
(panneau.querySelector('[data-focus-initial]') ?? panneau).focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Le contexte de toucheDuPlan : les géométries dessinées, leur régime et
|
|
|
|
|
// leur nombre de chaises.
|
|
|
|
|
function contexteClavier() {
|
|
|
|
|
const dessin = (id) => modele.tables.find((table) => table.id === id);
|
|
|
|
|
return {
|
|
|
|
|
geometries: modele.tables.map(({ geometrie }) => geometrie),
|
|
|
|
|
regimeDe: (id) => dessin(id).regime,
|
|
|
|
|
capaciteDe: (id) => dessin(id).geometrie.chaises.length,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// L'élément actif qu'annonce aria-activedescendant : le siège quand sa
|
|
|
|
|
// chaise est dessinée, sa table sinon.
|
|
|
|
|
const activeDescendant = $derived.by(() => {
|
|
|
|
|
if (actif === null || modele === null) return undefined;
|
|
|
|
|
const dessin = modele.tables.find(({ id }) => id === actif.table);
|
|
|
|
|
if (dessin === undefined) return undefined;
|
|
|
|
|
if (actif.siege !== null && dessin.chaises.some(({ siege }) => siege === actif.siege)) return `siege-${actif.table}-${actif.siege}`;
|
|
|
|
|
return `table-${actif.table}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// L'élément actif suit la place sélectionnée ; une sélection nouvelle
|
|
|
|
|
// vient dans le cadre, sauf celle d'un appui du pointeur, que l'effet
|
|
|
|
|
// selectionner d'un geste désigne ici par l'objet même qu'il confie à
|
|
|
|
|
// ui.selectionner. La sélection du montage ne déplace pas la vue.
|
|
|
|
|
let selectionSuivie = untrack(() => selection);
|
|
|
|
|
let selectionDuPointeur = null;
|
|
|
|
|
$effect(() => {
|
|
|
|
|
const place = selection;
|
|
|
|
|
if (place === selectionSuivie) return;
|
|
|
|
|
selectionSuivie = place;
|
|
|
|
|
const duPointeur = place !== null && place === selectionDuPointeur;
|
|
|
|
|
selectionDuPointeur = null;
|
|
|
|
|
if (place === null) return;
|
|
|
|
|
untrack(() => {
|
|
|
|
|
actif = { table: place.table, siege: place.siege ?? null };
|
|
|
|
|
if (!duPointeur) montrer(actif);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
// Restaure ce qu'un geste a montré.
|
|
|
|
|
function restaurer(cible) {
|
|
|
|
|
if (cible === 'vue') groupeVue.setAttribute('transform', transformDe(vue));
|
|
|
|
|
else if (cible === 'fantome') {
|
|
|
|
|
fantome = null;
|
|
|
|
|
elementFantome.removeAttribute('transform');
|
|
|
|
|
} else if (cible.genre === 'table') {
|
|
|
|
|
for (const element of elementsDeTable(cible.table)) element.removeAttribute('transform');
|
|
|
|
|
} else poignee = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vrai de l'envoi de la commande d'un geste à son règlement, la cible
|
|
|
|
|
// apres restaurée. La séance ne publie la charge qu'une fois l'écriture
|
|
|
|
|
// faite : jusque-là, l'état des places et les positions que lirait un
|
|
|
|
|
// nouvel appui sont ceux d'avant la commande, et la restauration de apres
|
|
|
|
|
// retirerait le transform d'un second glissement de la même table.
|
|
|
|
|
let commandeEnCours = false;
|
|
|
|
|
|
|
|
|
|
// Confie la commande d'un geste à la séance ; un refus se pose sur la
|
|
|
|
|
// place. La cible apres se restaure une fois la charge rendue : la table
|
|
|
|
|
// se dessine alors à sa nouvelle position, ou revient.
|
|
|
|
|
async function executerGeste({ commande, arguments: args, place, apres }) {
|
|
|
|
|
commandeEnCours = true;
|
|
|
|
|
try {
|
|
|
|
|
await seance.executer(commande, args);
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
ui.choisir(null);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
} catch (erreur) {
|
|
|
|
|
if (typeof erreur?.code !== 'string') throw erreur;
|
|
|
|
|
poserSurPlace(place, { code: erreur.code, details: erreur.details ?? {} }, []);
|
|
|
|
|
} finally {
|
|
|
|
|
try {
|
|
|
|
|
if (apres !== null) {
|
|
|
|
|
await tick();
|
|
|
|
|
restaurer(apres);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
commandeEnCours = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
$effect(() => {
|
|
|
|
|
const element = racine;
|
|
|
|
|
// La vue d'un changement de cadre : la vue initiale tant qu'il n'y en a
|
|
|
|
|
// pas, sinon la vue gardée dans les bornes du nouveau cadre. Elle se
|
|
|
|
|
// calcule à l'image suivante, sur le cadre relu alors : ce qu'elle fait
|
|
|
|
|
// paraître dans la surface — l'échelle, le bandeau — ne change pas le
|
|
|
|
|
// cadre pendant la livraison des observations de taille, et plusieurs
|
|
|
|
|
// changements d'une même image n'en font qu'un.
|
|
|
|
|
let image = null;
|
|
|
|
|
const recadrer = () => {
|
|
|
|
|
image = null;
|
|
|
|
|
const cadre = cadreCourant();
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
relireOrigine(cadre);
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
vue = vue === null ? vueInitiale(seance.cadrage(), englobantPlan, cadre) : vueApresCadre(vue, englobantPlan, cadre);
|
|
|
|
|
};
|
|
|
|
|
const observateur = new ResizeObserver(() => {
|
|
|
|
|
if (image === null) image = requestAnimationFrame(recadrer);
|
|
|
|
|
});
|
|
|
|
|
const surMolette = (evenement) => {
|
|
|
|
|
evenement.preventDefault();
|
|
|
|
|
poserVue(zoomerMolette(vue, englobantPlan, cadreCourant(), evenement));
|
|
|
|
|
};
|
|
|
|
|
observateur.observe(element);
|
|
|
|
|
element.addEventListener('wheel', surMolette, { passive: false });
|
|
|
|
|
return () => {
|
|
|
|
|
observateur.disconnect();
|
|
|
|
|
if (image !== null) cancelAnimationFrame(image);
|
|
|
|
|
element.removeEventListener('wheel', surMolette);
|
|
|
|
|
};
|
|
|
|
|
});
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
const element = racine;
|
|
|
|
|
let etatGeste = REPOS;
|
|
|
|
|
let contexte = null;
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// La minuterie de la pression longue, armée par l'appui d'un doigt.
|
|
|
|
|
let minuteriePression = null;
|
|
|
|
|
const oublierPression = () => {
|
|
|
|
|
if (minuteriePression !== null) horloge.annuler(minuteriePression);
|
|
|
|
|
minuteriePression = null;
|
|
|
|
|
};
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
// Le dernier point d'un mouvement en attente de son image.
|
|
|
|
|
let image = null;
|
|
|
|
|
let enAttente = null;
|
|
|
|
|
|
|
|
|
|
const oublierImage = () => {
|
|
|
|
|
if (image !== null) cancelAnimationFrame(image);
|
|
|
|
|
image = null;
|
|
|
|
|
enAttente = null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Applique un pas de la machine : son état, puis chaque effet.
|
|
|
|
|
function appliquer({ etat: suivant, effets }) {
|
|
|
|
|
const pointeur = etatGeste.pointeur;
|
|
|
|
|
etatGeste = suivant;
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// Un appui devenu glissement — au-delà du seuil, que la machine juge —
|
|
|
|
|
// n'est plus une pression longue.
|
|
|
|
|
if (suivant.phase === 'glisse') oublierPression();
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
for (const effet of effets) {
|
|
|
|
|
switch (effet.type) {
|
|
|
|
|
case 'capturer':
|
|
|
|
|
element.setPointerCapture(effet.pointeur);
|
|
|
|
|
document.addEventListener('keydown', surTouche, true);
|
|
|
|
|
break;
|
|
|
|
|
case 'debuterGeste':
|
|
|
|
|
seance.debuterGeste();
|
|
|
|
|
break;
|
|
|
|
|
case 'finirGeste':
|
|
|
|
|
oublierImage();
|
|
|
|
|
document.removeEventListener('keydown', surTouche, true);
|
|
|
|
|
if (element.hasPointerCapture(pointeur)) element.releasePointerCapture(pointeur);
|
|
|
|
|
seance.finirGeste();
|
|
|
|
|
break;
|
|
|
|
|
case 'ecrireTransform':
|
|
|
|
|
if (effet.cible === 'vue') groupeVue.setAttribute('transform', effet.transform);
|
|
|
|
|
else if (effet.cible === 'fantome') elementFantome.setAttribute('transform', effet.transform);
|
|
|
|
|
else for (const cible of elementsDeTable(effet.cible.table)) cible.setAttribute('transform', effet.transform);
|
|
|
|
|
break;
|
|
|
|
|
case 'montrerFantome':
|
|
|
|
|
fantome = { nom: nomDe(effet.participant), cle: cleFantome(etat.cible) };
|
|
|
|
|
break;
|
|
|
|
|
case 'montrerPlaces':
|
|
|
|
|
poignee = { table: effet.table, places: effet.places };
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
poserSurPlace({ table: effet.table }, ui.etat().pose?.refus ?? null, effet.avertissements);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
break;
|
|
|
|
|
case 'montrerRefus':
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
poserSurPlace(effet.place, effet.refus, ui.etat().pose?.avertissements ?? []);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
break;
|
|
|
|
|
case 'restaurer':
|
|
|
|
|
restaurer(effet.cible);
|
|
|
|
|
break;
|
|
|
|
|
case 'poserVue':
|
|
|
|
|
poserVue(effet.vue);
|
|
|
|
|
break;
|
|
|
|
|
case 'selectionner':
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
selectionDuPointeur = effet.place;
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
ui.selectionner(effet.place);
|
|
|
|
|
break;
|
|
|
|
|
case 'executer':
|
|
|
|
|
executerGeste(effet);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new TypeError(`effet de geste inconnu : ${JSON.stringify(effet.type)}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const annulerGeste = (cause) => {
|
|
|
|
|
if (etatGeste.phase !== 'repos') appliquer(geste(etatGeste, { type: 'annuler', cause }, contexte));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function surTouche(evenement) {
|
|
|
|
|
if (evenement.key !== 'Escape' || etatGeste.phase === 'repos') return;
|
|
|
|
|
evenement.stopPropagation();
|
|
|
|
|
evenement.preventDefault();
|
|
|
|
|
annulerGeste('echap');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Un appui au repos, du bouton principal, sur un plan dessiné. Tant
|
|
|
|
|
// qu'une commande d'un geste n'est pas réglée, seul le sol se saisit :
|
|
|
|
|
// le déplacement de vue ne lit ni la charge ni l'état des places.
|
|
|
|
|
const surAppui = (evenement) => {
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
auClavier = false;
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
if (etatGeste.phase !== 'repos' || evenement.button !== 0 || vue === null || modele === null) return;
|
|
|
|
|
const source = sourceDe(evenement.target);
|
|
|
|
|
if (commandeEnCours && source.genre !== 'sol') return;
|
|
|
|
|
const cadre = cadreCourant();
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
relireOrigine(cadre);
|
|
|
|
|
ui.poserSurPlace(null);
|
|
|
|
|
survol = null;
|
|
|
|
|
pression = null;
|
|
|
|
|
oublierPression();
|
|
|
|
|
// Un doigt posé sur une table : sa pression longue ouvre l'infobulle.
|
|
|
|
|
if (evenement.pointerType === 'touch' && source.table !== null) {
|
|
|
|
|
const table = source.table;
|
|
|
|
|
minuteriePression = horloge.planifier(() => {
|
|
|
|
|
minuteriePression = null;
|
|
|
|
|
pression = table;
|
|
|
|
|
}, DUREE_PRESSION);
|
|
|
|
|
}
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
// La charge, l'état, les géométries et k du moment de l'appui : le
|
|
|
|
|
// geste se juge sur eux jusqu'à son terme.
|
|
|
|
|
const chargeDuGeste = charge;
|
|
|
|
|
const etatDuGeste = etat;
|
|
|
|
|
const geometries = modele.tables.map((table) => table.geometrie);
|
|
|
|
|
const k = vue.k;
|
|
|
|
|
contexte = {
|
|
|
|
|
controlerPlaces: memoriser((table, places) => controlerCapacite(chargeDuGeste, table, places, { origine: 'poignee' })),
|
|
|
|
|
deposer: (source, point) =>
|
|
|
|
|
deposer({
|
|
|
|
|
etatPlaces: etatDuGeste,
|
|
|
|
|
tour: etatDuGeste.tour,
|
|
|
|
|
personne: source.participant,
|
|
|
|
|
depuis: { table: source.table, siege: source.siege },
|
|
|
|
|
point,
|
|
|
|
|
geometries,
|
|
|
|
|
k,
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
const point = { x: evenement.clientX, y: evenement.clientY };
|
|
|
|
|
appliquer(geste(etatGeste, { type: 'presser', source, pointeur: evenement.pointerId, point, vue, cadre }, contexte));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const livrer = () => {
|
|
|
|
|
image = null;
|
|
|
|
|
const point = enAttente;
|
|
|
|
|
enAttente = null;
|
|
|
|
|
if (point !== null && etatGeste.phase !== 'repos') {
|
|
|
|
|
appliquer(geste(etatGeste, { type: 'bouger', pointeur: etatGeste.pointeur, point }, contexte));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const surMouvement = (evenement) => {
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
if (etatGeste.phase === 'repos') {
|
|
|
|
|
// Le survol : une souris ou un stylet sans bouton enfoncé.
|
|
|
|
|
if (evenement.pointerType !== 'touch' && evenement.buttons === 0) {
|
|
|
|
|
const groupe = evenement.target.closest('[data-table]');
|
|
|
|
|
survol = groupe === null ? null : Number(groupe.dataset.table);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (evenement.pointerId !== etatGeste.pointeur) return;
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
enAttente = { x: evenement.clientX, y: evenement.clientY };
|
|
|
|
|
if (image === null) image = requestAnimationFrame(livrer);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const surRelache = (evenement) => {
|
|
|
|
|
if (etatGeste.phase === 'repos' || evenement.pointerId !== etatGeste.pointeur) return;
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
oublierPression();
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
oublierImage();
|
|
|
|
|
const point = { x: evenement.clientX, y: evenement.clientY };
|
|
|
|
|
appliquer(geste(etatGeste, { type: 'relacher', pointeur: evenement.pointerId, point }, contexte));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const surAnnulationPointeur = (evenement) => {
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
oublierPression();
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
if (etatGeste.phase !== 'repos' && evenement.pointerId === etatGeste.pointeur) annulerGeste('pointercancel');
|
|
|
|
|
};
|
|
|
|
|
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
// La sortie du pointeur et la perte du focus arrivent aussi pendant
|
|
|
|
|
// qu'un bloc démonte le plan — Chromium retire le focus d'un élément
|
|
|
|
|
// qu'on détache — : l'état change hors du suivi des dépendances.
|
|
|
|
|
const surSortie = () => {
|
|
|
|
|
untrack(() => {
|
|
|
|
|
survol = null;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Le focus de la racine. Venu du clavier, il active la sélection, sinon
|
|
|
|
|
// la première table, quand rien n'est actif, et amène l'élément actif
|
|
|
|
|
// dans le cadre. Un appui, qui donne aussi le focus, n'écrit rien sous
|
|
|
|
|
// la racine et ne déplace pas la vue : la première touche activera.
|
|
|
|
|
const surFocus = () => {
|
|
|
|
|
aLeFocus = true;
|
|
|
|
|
auClavier = element.matches(':focus-visible');
|
|
|
|
|
if (!auClavier || modele === null || modele.tables.length === 0) return;
|
|
|
|
|
const existe = actif !== null && modele.tables.some(({ id }) => id === actif.table);
|
|
|
|
|
if (!existe) {
|
|
|
|
|
const place = selection ?? { table: modele.tables[0].id, siege: null };
|
|
|
|
|
actif = { table: place.table, siege: place.siege ?? null };
|
|
|
|
|
}
|
|
|
|
|
if (etatGeste.phase === 'repos') montrer(actif);
|
|
|
|
|
};
|
|
|
|
|
const surPerteFocus = () => {
|
|
|
|
|
untrack(() => {
|
|
|
|
|
aLeFocus = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Une touche sur la racine, hors d'un geste : Échap d'un geste en cours
|
|
|
|
|
// passe d'abord par surTouche, en phase de capture.
|
|
|
|
|
const surToucheClavier = (evenement) => {
|
|
|
|
|
if (etatGeste.phase !== 'repos' || modele === null || evenement.altKey || evenement.ctrlKey || evenement.metaKey) return;
|
|
|
|
|
auClavier = true;
|
|
|
|
|
if (evenement.key === 'Escape') ui.choisir(null);
|
|
|
|
|
const rendu = toucheDuPlan(actif, evenement.key, contexteClavier());
|
|
|
|
|
if (rendu === null) return;
|
|
|
|
|
evenement.preventDefault();
|
|
|
|
|
actif = rendu.actif;
|
|
|
|
|
const { effet } = rendu;
|
|
|
|
|
if (effet === null) {
|
|
|
|
|
montrer(actif);
|
|
|
|
|
} else if (effet.type === 'selectionner') {
|
|
|
|
|
ui.selectionner(effet.place);
|
|
|
|
|
focaliserPanneau('place');
|
|
|
|
|
} else if (effet.type === 'ouvrirTable') {
|
|
|
|
|
ui.selectionner({ table: effet.table });
|
|
|
|
|
focaliserPanneau('table');
|
|
|
|
|
} else {
|
|
|
|
|
ui.poserSurPlace(null);
|
|
|
|
|
ui.selectionner(null);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
const desabonner = seance.surAnnulation(() => annulerGeste('lecture'));
|
|
|
|
|
element.addEventListener('pointerdown', surAppui);
|
|
|
|
|
element.addEventListener('pointermove', surMouvement);
|
|
|
|
|
element.addEventListener('pointerup', surRelache);
|
|
|
|
|
element.addEventListener('pointercancel', surAnnulationPointeur);
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
element.addEventListener('pointerleave', surSortie);
|
|
|
|
|
element.addEventListener('focus', surFocus);
|
|
|
|
|
element.addEventListener('blur', surPerteFocus);
|
|
|
|
|
element.addEventListener('keydown', surToucheClavier);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
return () => {
|
|
|
|
|
desabonner();
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
oublierPression();
|
|
|
|
|
element.removeEventListener('pointerleave', surSortie);
|
|
|
|
|
element.removeEventListener('focus', surFocus);
|
|
|
|
|
element.removeEventListener('blur', surPerteFocus);
|
|
|
|
|
element.removeEventListener('keydown', surToucheClavier);
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
element.removeEventListener('pointerdown', surAppui);
|
|
|
|
|
element.removeEventListener('pointermove', surMouvement);
|
|
|
|
|
element.removeEventListener('pointerup', surRelache);
|
|
|
|
|
element.removeEventListener('pointercancel', surAnnulationPointeur);
|
|
|
|
|
oublierImage();
|
|
|
|
|
document.removeEventListener('keydown', surTouche, true);
|
|
|
|
|
if (etatGeste.phase !== 'repos') seance.finirGeste();
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// La place qui porte la pose la désigne par aria-describedby ; l'effet
|
|
|
|
|
// suit le modèle, qui peut redessiner la chaise.
|
|
|
|
|
$effect(() => {
|
|
|
|
|
if (poseAffichee === null || modele === null) return;
|
|
|
|
|
const element = elementDePlace(pose.place);
|
|
|
|
|
if (element === null) return;
|
|
|
|
|
element.setAttribute('aria-describedby', idPose);
|
|
|
|
|
return () => element.removeAttribute('aria-describedby');
|
|
|
|
|
});
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div class="plan">
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
<BarreVue {tour} {tours} {zoomer} {ajuster} {evidence} />
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
<div class="surface" data-surface bind:this={surface}>
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
<BandeauPlan bandeau={modele === null ? [] : modele.bandeau} />
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
<!-- Le plan est un composant interactif à deux dimensions, que le
|
|
|
|
|
clavier parcourt par aria-activedescendant : role="application",
|
|
|
|
|
que le compilateur range parmi les rôles non interactifs. -->
|
|
|
|
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
|
|
|
<svg
|
|
|
|
|
class="dessin"
|
|
|
|
|
data-plan
|
|
|
|
|
tabindex="0"
|
|
|
|
|
role="application"
|
|
|
|
|
aria-roledescription={t('plan.vue.role')}
|
|
|
|
|
aria-label={t('plan.vue.dessin')}
|
|
|
|
|
aria-activedescendant={activeDescendant}
|
|
|
|
|
aria-describedby={infobulleDuFocus ? idInfobulle : undefined}
|
|
|
|
|
bind:this={racine}
|
|
|
|
|
>
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
<g data-vue transform={vue === null ? undefined : transformDe(vue)} bind:this={groupeVue}>
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
{#if modele !== null}
|
|
|
|
|
<PlanDessin {modele} />
|
|
|
|
|
{/if}
|
|
|
|
|
</g>
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
<Fantome {fantome} bind:element={elementFantome} />
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
</svg>
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
{#if poseAffichee !== null}
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
<RefusPlace pose={poseAffichee} fermer={fermerPose} gestes={gestesPose} />
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
{/if}
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
<Infobulle {infobulle} id={idInfobulle} />
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
<!-- L'échelle occupe sa place avant que la vue n'existe, cachée : sa
|
|
|
|
|
venue ne change pas le cadre du dessin. -->
|
|
|
|
|
<div class="pied" class:cache={vue === null}>
|
|
|
|
|
<EchellePlan k={vue === null ? 1 : vue.k} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.plan {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--espace-2);
|
|
|
|
|
min-width: 0;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.surface {
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
position: relative;
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
display: flex;
|
|
|
|
|
flex: 1 1 auto;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--espace-2);
|
|
|
|
|
min-height: 0;
|
|
|
|
|
touch-action: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pied.cache {
|
|
|
|
|
visibility: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ni bordure, ni remplissage, ni viewBox : un pixel CSS du <svg> est un
|
|
|
|
|
pixel de l'écran, et le cadre est sa boîte (§ 7.2). */
|
|
|
|
|
.dessin {
|
|
|
|
|
display: block;
|
|
|
|
|
flex: 1 1 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
border: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
touch-action: none;
|
[ADD] interface: plan gestures — drag a person, a table, a handle
The plan is where the operator changes the seating. A drag starts only
past a movement threshold, so a click is never a drag (§ 8.4), and Escape
cancels it; a person dropped on a chair is moved or swapped through the
places commands, a table is moved, and a handle resizes it after the
capacity check. A refusal appears on the place where the gesture ended.
A new gesture waits for the previous write, so a second drag never starts
from a stale plan, and nothing reads layout while a drag runs.
Checked: 2499 node and 436 browser tests.
--- FR ---
[ADD] interface : gestes du plan — glisser une personne, une table
Le plan est l'endroit où l'opérateur change le placement. Un glissement ne
commence qu'au-delà d'un seuil de mouvement — un clic n'en est jamais un
(§ 8.4) — et Échap l'annule ; une personne déposée sur une chaise est
déplacée ou échangée par les commandes des places, une table se déplace,
et une poignée la redimensionne après le contrôle de capacité. Un refus se
pose sur la place où le geste a fini. Un geste attend l'écriture du
précédent, si bien qu'un second glissement ne part jamais d'un plan
périmé, et rien ne lit la mise en page pendant un glissement.
Vérifié : 2499 node et 436 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 10:01:21 -04:00
|
|
|
user-select: none;
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
}
|
[ADD] interface: plan keyboard, tooltip, side panels, person menu, lists
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
2026-10-07 11:08:59 -04:00
|
|
|
|
|
|
|
|
/* Un focus venu du clavier se voit sur la racine, contour tracé en
|
|
|
|
|
dedans : un contour ne change pas la boîte du cadre. L'élément actif
|
|
|
|
|
porte en plus la marque focus. */
|
|
|
|
|
.dessin:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dessin:focus-visible {
|
|
|
|
|
outline: 3px solid var(--couleur-focus);
|
|
|
|
|
outline-offset: -3px;
|
|
|
|
|
}
|
[ADD] interface: plan view — zoom, framing, fit, scale, side panels
The drawing becomes a plan the operator can look around. One transform on
the drawing group holds the view, so the screen and the drawing share the
formula of src/geometrie; the wheel zooms about the pointer without
scrolling the page, « + », « − » and « Ajuster » bound the scale, and a
resize keeps the view inside its limits. Only a deliberate gesture saves
the framing, deferred, into reglages_locaux.json — in read mode and on a
blocked plan too. The side panels take their first form.
Checked: 2368 node and 378 browser tests.
--- FR ---
[ADD] interface : vue du plan — zoom, cadrage, ajuster, échelle
Le dessin devient un plan que l'opérateur parcourt. Une seule
transformation du groupe de dessin tient la vue : l'écran et le dessin
partagent la formule de src/geometrie ; la molette zoome autour du
pointeur sans faire défiler la page, « + », « − » et « Ajuster » bornent
l'échelle, et un redimensionnement garde la vue dans ses limites. Seul un
geste voulu enregistre le cadrage, en différé, dans reglages_locaux.json
— en lecture et sur un plan bloqué aussi. Les panneaux latéraux prennent
leur première forme.
Vérifié : 2368 node et 378 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:09:45 -04:00
|
|
|
</style>
|