Lot U3 : l'interface de l'Acte d'engagement (Yvv, 03/11) — avant le texte, hors vote, « Info ouverture doc de réf. » (OpeningInfo, lib/opening.ts) : le paragraphe d'Yvv mot pour mot à la place de doc.empty.seeds (sa première phrase tant qu'aucun article n'est en vigueur, puis « entrent en vigueur » où « y » n'avait plus rien à reprendre), le cran d'ouverture des Propositions d'amorce, du titre, de l'ordre de chaque section et des autres articles (avant tout changement voté) et le quorum de chaque cran, lus dans l'API ; une séparation (un filet de la couleur de la clé de voûte, jamais une bordure) ; plus les titres « Introduction » et « Engagements fondamentaux » dans le texte. Les informations de F1, F1.1, F1.2, N1, N1.1 et O1 là où elles servent : vote.permanent, vote.rival, vote.others.rule, formula.Tprime, formula.round, formula.B.wot, formula.G, settings.share.note, settings.usual.quorum, settings.gradient, order.edit.effect ; les termes définis de T3c (« à la participation habituelle », « quorum de l'article », « part exigée », « repose sur moins de N propositions votées depuis un an »), plus de « comme d'habitude ». « Proposition d'amorce » (Starter proposal, Propuesta de arranque). Le mot d'Yvv pour qui n'est pas invité, à l'accueil sans session (à la place du bouton Ğ1 tant que la connexion Ğ1 n'est pas active) et sur « Se connecter » (signin.g1.pending sans redite). Réordonner : la ligne sous le pointeur lue dans la mise en page (offsetTop), pas là où animate:flip la montre — une poignée tirée sur plusieurs lignes ne revient plus en arrière ; le pointeur capturé par la liste. API : seedFeatured sur chaque version (seul changement sous crates/). C1, C3, C20, C27, C35, C38 suivent les mots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-11-03 22:28:41 +01:00
co-authored by Claude Opus 5.5
parent 9d478d668a
commit 8528a269fc
20 changed files with 434 additions and 88 deletions
+21 -7
View File
@@ -111,11 +111,13 @@
}
}
// Dragging by the handle: the article takes the place of the row under the pointer.
// Dragging by the handle: the article takes the place of the row under the pointer. The pointer
// is captured by the list, which stays put, rather than by the handle of a row the reorder moves
// (a guard: what lot U3 found is the measure of the rows, in `drag`).
function grab(e: PointerEvent, id: string) {
if (busy || e.button !== 0) return;
e.preventDefault();
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
root?.setPointerCapture(e.pointerId);
dragging = id;
chosen = null;
error = '';
@@ -124,9 +126,12 @@
function drag(e: PointerEvent) {
const id = dragging;
if (!id || !root) return;
// The rows where the layout puts them, not where their animation shows them (`animate:flip`
// moves them by a transform): a row sliding under the pointer would send the article back.
const y = e.clientY - root.getBoundingClientRect().top;
for (const row of root.querySelectorAll<HTMLElement>('li[data-id]')) {
const box = row.getBoundingClientRect();
if (e.clientY < box.top || e.clientY >= box.bottom) continue;
const top = row.offsetTop - root.offsetTop;
if (y < top || y >= top + row.offsetHeight) continue;
const target = row.dataset.id;
if (target && target !== id) list = place(list, id, target);
return;
@@ -164,11 +169,20 @@
<div class="reorder">
<p class="faint small" id="order-help-{unit.id}">{t('order.edit.help')}</p>
<ol class="list" aria-label={t('order.edit.label')} aria-describedby="order-help-{unit.id}" bind:this={root}>
<ol
class="list"
aria-label={t('order.edit.label')}
aria-describedby="order-help-{unit.id}"
bind:this={root}
onpointermove={drag}
onpointerup={drop}
onpointercancel={drop}
onlostpointercapture={drop}
>
{#each list as id, i (id)}
{@const name = nameOf(id)}
<li data-id={id} class:chosen={chosen === id} class:moved={changed.has(id)} class:dragging={dragging === id} animate:flip={{ duration: reduce ? 0 : 140 }}>
<span class="grip" aria-hidden="true" onpointerdown={(e) => grab(e, id)} onpointermove={drag} onpointerup={drop} onpointercancel={drop}>
<span class="grip" aria-hidden="true" onpointerdown={(e) => grab(e, id)}>
<Icon name="grip" size={18} />
</span>
<button type="button" class="item" data-id={id} aria-pressed={chosen === id} disabled={busy} onclick={() => pick(id)} onkeydown={(e) => key(e, id)}>
@@ -186,7 +200,7 @@
{/each}
</ol>
<p class="visually-hidden" aria-live="polite">{said}</p>
<p class="faint small">{t('order.edit.note', { level: inertiaLevel(unit.inertia) })} {t('module.order.rule')}</p>
<p class="faint small">{t('order.edit.note', { level: inertiaLevel(unit.inertia) })} {t('order.edit.effect')} {t('module.order.rule')}</p>
{#if existing && !unchanged}<p class="note" role="status">{t('order.edit.exists')}</p>{/if}
{#if error}<p class="warn" role="alert">{error}</p>{/if}
<div class="actions">