forked from yvv/decision
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:
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
// The open proposals, one line per article (Yvv, 2026-10-30). An article with a
|
||||
// single version opens its vote; one with several shows the best of them and
|
||||
// unfolds the others, each with its label, its water level and its marks — and
|
||||
// each opens its own vote.
|
||||
// single version unfolds its vote below it; one with several shows the best of
|
||||
// them and unfolds the others, each with its label, its water level and its
|
||||
// marks — and each unfolds its own vote.
|
||||
import type { Snippet } from 'svelte';
|
||||
import { plain } from '$lib/markdown';
|
||||
import { inertiaBadge, relationOf, titleOf } from '$lib/labels';
|
||||
import { i18n, t, type Key } from '$lib/i18n/index.svelte';
|
||||
@@ -13,16 +14,26 @@
|
||||
|
||||
let {
|
||||
articles,
|
||||
selected,
|
||||
onopen,
|
||||
}: { articles: Article[]; selected: string | null; onopen: (unit: Unit, p: Proposal) => void } = $props();
|
||||
vote,
|
||||
ontoggle,
|
||||
fold,
|
||||
onsection,
|
||||
}: {
|
||||
articles: Article[];
|
||||
/** The version whose vote is unfolded. */
|
||||
vote: string | null;
|
||||
ontoggle: (unit: Unit, p: Proposal) => void;
|
||||
fold: Snippet<[Unit, Proposal]>;
|
||||
/** Opens the basket on a section; absent when this session does not vote here. */
|
||||
onsection?: (section: string) => void;
|
||||
} = $props();
|
||||
|
||||
const SECTIONS = ['introduction', 'fondamental', 'technique', 'conclusion', 'annexe', 'formule', 'inertie', 'ordonnancement'];
|
||||
const sectionLabel = (s: string | null) => (s && SECTIONS.includes(s) ? t(`section.${s}` as Key) : null);
|
||||
|
||||
// Unfolded by hand, or because the version open in the panel is inside.
|
||||
// Unfolded by hand, or because the version whose vote is unfolded is inside.
|
||||
let unfolded = $state<Record<string, boolean>>({});
|
||||
const isOpen = (a: Article) => unfolded[a.unit.id] ?? a.versions.some((p) => p.id === selected);
|
||||
const isOpen = (a: Article) => unfolded[a.unit.id] ?? a.versions.some((p) => p.id === vote);
|
||||
const toggle = (a: Article) => (unfolded[a.unit.id] = !isOpen(a));
|
||||
|
||||
/** The title of the article: its best text version, or what the best proposal is. */
|
||||
@@ -61,11 +72,17 @@
|
||||
{@const unit = a.unit}
|
||||
{@const best = a.versions[0]}
|
||||
{#if sectionLabel(unit.section) && unit.section !== articles[i - 1]?.unit.section}
|
||||
<li class="section eyebrow">{sectionLabel(unit.section)}</li>
|
||||
<li class="section">
|
||||
<span class="eyebrow">{sectionLabel(unit.section)}</span>
|
||||
{#if onsection && unit.section}
|
||||
{@const key = unit.section}
|
||||
<button type="button" class="vote-section" onclick={() => onsection(key)}>{t('basket.section')}</button>
|
||||
{/if}
|
||||
</li>
|
||||
{/if}
|
||||
{#if best && a.versions.length === 1}
|
||||
<li class="article">
|
||||
<button type="button" class="row" class:current={selected === best.id} onclick={() => onopen(unit, best)}>
|
||||
<li class="article" class:open={vote === best.id}>
|
||||
<button type="button" class="row" class:current={vote === best.id} aria-expanded={vote === best.id} aria-controls="vote-{best.id}" onclick={() => ontoggle(unit, best)}>
|
||||
<WaterLevel tally={best.tally} evaluation={best.evaluation} blocked={best.blocked} />
|
||||
<span class="main">
|
||||
<span class="title">{titleOf(best, unit)}</span>
|
||||
@@ -78,6 +95,7 @@
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{#if vote === best.id}<div class="fold">{@render fold(unit, best)}</div>{/if}
|
||||
</li>
|
||||
{:else if best}
|
||||
{@const open = isOpen(a)}
|
||||
@@ -105,7 +123,7 @@
|
||||
<ul class="versions" id="versions-{unit.id}" hidden={!open}>
|
||||
{#each a.versions as p (p.id)}
|
||||
<li>
|
||||
<button type="button" class="version" class:current={selected === p.id} onclick={() => onopen(unit, p)}>
|
||||
<button type="button" class="version" class:current={vote === p.id} aria-expanded={vote === p.id} aria-controls="vote-{p.id}" onclick={() => ontoggle(unit, p)}>
|
||||
<WaterLevel tally={p.tally} evaluation={p.evaluation} blocked={p.blocked} />
|
||||
<span class="main">
|
||||
<span class="label">{labelOf(p, a)}{#if p.kind === 'text' && p.title !== title}<span class="other"> · {p.title}</span>{/if}</span>
|
||||
@@ -116,6 +134,7 @@
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{#if vote === p.id}{@render fold(unit, p)}{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
@@ -234,9 +253,30 @@
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
.section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.3rem 1rem;
|
||||
margin: 1.6rem 0 0.3rem;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
.vote-section {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--water);
|
||||
font-weight: 750;
|
||||
font-size: 0.84rem;
|
||||
padding: 0.3rem 0.7rem;
|
||||
min-height: 2.25rem;
|
||||
border-radius: var(--r-pill);
|
||||
}
|
||||
.vote-section:hover {
|
||||
background: var(--water-wash);
|
||||
}
|
||||
.fold {
|
||||
padding: 0 1rem 1rem;
|
||||
}
|
||||
.section:first-child {
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user