v3 : le vote déplié sous l'article, le panier, la signature par le portefeuille Ğ1

- Le vote se déplie sous l'article, façon accordéon, l'URL pour état (?vote=) :
  niveau d'eau, totaux, seuil et voix manquantes, ton vote et où il en est
  (enregistré, inscrit sur la chaîne, en attente, à reconfirmer, non compté),
  inertie et formule. Le reste d'une version (coquilles, traductions,
  signalements, historique) passe dans l'onglet « Détails » du panneau.
- Le panier : article par article, « tout pour / tout contre » par section ou
  pour tout le document, jamais pour un article à plusieurs versions ; un seul
  geste (une écriture, ou une transaction).
- La signature : le portefeuille de la page (injectedWeb3), polkadot-api chargé
  au moment de signer ; sans portefeuille, Ğecko par lien et QR ; la page
  attend la confirmation. Un bulletin que le portefeuille n'a pas signé est
  lâché aussitôt (DELETE /api/ballots/{id}).
- Pastilles d'ambiance : flèches dans le groupe, anneau de focus pointillé à
  la couleur d'accent de la page (3:1 au moins), distinct du choix.
- e2e : chaîne simulée (indexeur et nœud), faux portefeuille ; C21–C24, et
  C4, C5, C6, C11, C17, C19, C20, M1 mis à jour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-30 22:47:26 +01:00
co-authored by Claude Opus 5.5
parent 3541e1b1a6
commit b260596cfd
36 changed files with 4353 additions and 234 deletions
+19 -6
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import type { Shown } from '$lib/doc.svelte';
import { i18n, t } from '$lib/i18n/index.svelte';
import Prose from './Prose.svelte';
@@ -11,6 +12,9 @@
rivals = 0,
selected = false,
actions = true,
voteFor = null,
voteOpen = false,
fold,
onaction,
}: {
text: Shown;
@@ -20,11 +24,16 @@
rivals?: number;
selected?: boolean;
actions?: boolean;
onaction: (tab: 'vote' | 'proposals' | 'propose') => void;
/** The version « Le vote » unfolds, and whether it is unfolded. */
voteFor?: string | null;
voteOpen?: boolean;
/** The vote, unfolded under the text. */
fold?: Snippet;
onaction: (action: 'vote' | 'proposals' | 'propose') => void;
} = $props();
</script>
<article class="unit {mark}" class:selected>
<article class="unit {mark}" class:selected class:unfolded={voteOpen}>
<header>
<h2 class="eyebrow">{text.title}</h2>
{#if mark !== 'unchanged'}<span class="tag {mark === 'removed' ? 'ochre' : 'moss'}">{t(`doc.mark.${mark}`)}</span>{/if}
@@ -38,12 +47,13 @@
{:else if enteredAt}{t('doc.adopted', { date: i18n.date(enteredAt) })}{/if}
</span>
<span class="links">
<button type="button" onclick={() => onaction('vote')}>{t('doc.action.vote')}</button>
<button type="button" class:on={voteOpen} aria-expanded={voteOpen} aria-controls={voteFor ? `vote-${voteFor}` : undefined} onclick={() => onaction('vote')}>{t('doc.action.vote')}</button>
<button type="button" onclick={() => onaction('proposals')}>{t('doc.action.proposals')}{#if rivals > 0} · {rivals}{/if}</button>
<button type="button" onclick={() => onaction('propose')}>{t('doc.action.propose')}</button>
</span>
</footer>
{/if}
{#if voteOpen && fold}{@render fold()}{/if}
</article>
<style>
@@ -59,7 +69,8 @@
}
.unit:hover,
.unit:focus-within,
.unit.selected {
.unit.selected,
.unit.unfolded {
background: var(--surface);
box-shadow: var(--shadow);
}
@@ -98,7 +109,8 @@
}
.unit:hover .links,
.unit:focus-within .links,
.unit.selected .links {
.unit.selected .links,
.unit.unfolded .links {
opacity: 1;
}
.links button {
@@ -110,7 +122,8 @@
min-height: 2.25rem;
border-radius: var(--r-pill);
}
.links button:hover {
.links button:hover,
.links button.on {
background: var(--water-wash);
}
@media (hover: none) {