[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).
|
[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.
|
|
|
|
|
import { getContext, tick } from 'svelte';
|
|
|
|
|
import { controlerCapacite } from '../../application/capacite.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 { etatPlaces } from '../../application/places.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 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 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 { modeleDessin } from './modele.js';
|
|
|
|
|
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,
|
|
|
|
|
tourAffiche,
|
|
|
|
|
vueApresCadre,
|
|
|
|
|
vueInitiale,
|
|
|
|
|
zoomerBouton,
|
|
|
|
|
zoomerMolette,
|
|
|
|
|
} from './vue_plan.js';
|
|
|
|
|
|
|
|
|
|
let { diagnostic = null } = $props();
|
|
|
|
|
|
|
|
|
|
const t = getContext('t');
|
|
|
|
|
const seance = getContext('seance');
|
|
|
|
|
const ui = getContext('ui');
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
// Le refus ou les avertissements posés sur une place : {place, refus,
|
|
|
|
|
// avertissements, ancre — le point de dessin de la place, cm —, origine —
|
|
|
|
|
// le coin du <svg> dans la surface, px —}, ou null.
|
|
|
|
|
let pose = $state.raw(null);
|
|
|
|
|
|
|
|
|
|
const prefixe = $props.id();
|
|
|
|
|
const idPose = `${prefixe}-pose`;
|
[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);
|
|
|
|
|
const tour = $derived(tourAffiche($ui.tour, tours));
|
|
|
|
|
const etat = $derived(etatPlaces(charge, { tour, proposition: $ui.proposition }));
|
|
|
|
|
const englobantPlan = $derived(englobantDuTour(etat));
|
|
|
|
|
const nomDe = $derived(nomDeCharge(charge));
|
|
|
|
|
const modele = $derived(
|
|
|
|
|
vue === null
|
|
|
|
|
? 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
|
|
|
: modeleDessin(etat, { k: vue.k, listes: $ui.listes, miseEnEvidence: $ui.miseEnEvidence, nomDe, diagnostic, poignee }),
|
[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 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 pose à sa position dans la surface, sous la vue courante.
|
|
|
|
|
const poseAffichee = $derived.by(() => {
|
|
|
|
|
if (pose === null || vue === null) return null;
|
|
|
|
|
const { place, refus, avertissements, ancre, origine } = pose;
|
|
|
|
|
const { x, y } = ecranDepuisDessin(vue, origine, ancre.x, ancre.y);
|
|
|
|
|
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 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}"]`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Le point de dessin d'une place : le centre de sa chaise, ou de sa table.
|
|
|
|
|
function ancreDe({ table, siege = null }) {
|
|
|
|
|
const { geometrie } = modele.tables.find(({ id }) => id === table);
|
|
|
|
|
const chaise = siege === null ? undefined : geometrie.chaises[siege - 1];
|
|
|
|
|
return chaise === undefined ? { ...geometrie.centre } : { x: chaise.x, y: chaise.y };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Le coin du <svg> dans la surface, relu à l'appui : un cadre dont le
|
|
|
|
|
// bord gauche et le haut sont relatifs à la surface.
|
|
|
|
|
let origineSurface = { gauche: 0, haut: 0, largeur: 0, hauteur: 0 };
|
|
|
|
|
|
|
|
|
|
// Pose un refus et des avertissements sur une place ; rien des deux
|
|
|
|
|
// retire la pose.
|
|
|
|
|
function poserSurPlace(place, refus, avertissements) {
|
|
|
|
|
if (refus === null && avertissements.length === 0) {
|
|
|
|
|
pose = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pose = { place, refus, avertissements, ancre: ancreDe(place), origine: origineSurface };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fermerPose = () => {
|
|
|
|
|
pose = null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
} 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();
|
|
|
|
|
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;
|
|
|
|
|
// 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;
|
|
|
|
|
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 };
|
|
|
|
|
poserSurPlace({ table: effet.table }, pose?.refus ?? null, effet.avertissements);
|
|
|
|
|
break;
|
|
|
|
|
case 'montrerRefus':
|
|
|
|
|
poserSurPlace(effet.place, effet.refus, pose?.avertissements ?? []);
|
|
|
|
|
break;
|
|
|
|
|
case 'restaurer':
|
|
|
|
|
restaurer(effet.cible);
|
|
|
|
|
break;
|
|
|
|
|
case 'poserVue':
|
|
|
|
|
poserVue(effet.vue);
|
|
|
|
|
break;
|
|
|
|
|
case 'selectionner':
|
|
|
|
|
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) => {
|
|
|
|
|
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();
|
|
|
|
|
const rect = surface.getBoundingClientRect();
|
|
|
|
|
origineSurface = { gauche: cadre.gauche - rect.left, haut: cadre.haut - rect.top, largeur: cadre.largeur, hauteur: cadre.hauteur };
|
|
|
|
|
pose = null;
|
|
|
|
|
// 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) => {
|
|
|
|
|
if (etatGeste.phase === 'repos' || evenement.pointerId !== etatGeste.pointeur) return;
|
|
|
|
|
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;
|
|
|
|
|
oublierImage();
|
|
|
|
|
const point = { x: evenement.clientX, y: evenement.clientY };
|
|
|
|
|
appliquer(geste(etatGeste, { type: 'relacher', pointeur: evenement.pointerId, point }, contexte));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const surAnnulationPointeur = (evenement) => {
|
|
|
|
|
if (etatGeste.phase !== 'repos' && evenement.pointerId === etatGeste.pointeur) annulerGeste('pointercancel');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const desabonner = seance.surAnnulation(() => annulerGeste('lecture'));
|
|
|
|
|
element.addEventListener('pointerdown', surAppui);
|
|
|
|
|
element.addEventListener('pointermove', surMouvement);
|
|
|
|
|
element.addEventListener('pointerup', surRelache);
|
|
|
|
|
element.addEventListener('pointercancel', surAnnulationPointeur);
|
|
|
|
|
return () => {
|
|
|
|
|
desabonner();
|
|
|
|
|
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">
|
|
|
|
|
<BarreVue {tour} {tours} {zoomer} {ajuster} />
|
[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} />
|
|
|
|
|
<svg class="dessin" data-plan aria-label={t('plan.vue.dessin')} 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}
|
|
|
|
|
<RefusPlace pose={poseAffichee} fermer={fermerPose} />
|
|
|
|
|
{/if}
|
[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
|
|
|
}
|
|
|
|
|
</style>
|