forked from yvv/decision
v3 : modules d'ordre et de titre — l'interface, restes mineurs (L11, C25–C27)
Chaque section du lecteur finit par l'ordre de ses articles (inertie 2) : ses articles numérotés, son vote déplié, « Proposer un autre ordre » — une liste où l'on clique un article puis sa place, ses flèches, ou le clavier. Le titre du document (inertie 4) se vote et se propose sous le titre, « traduction à revoir » quand il a changé. Les deux sont dans la liste des propositions, le panier et la chaîne Ğ1 ; un nouvel article se propose dans une section, à sa fin. La liste du groupe compte les propositions ouvertes des modules. Restes mineurs : la participation ramenée se dit en deux temps (la mesure, puis ce que le groupe demande), les lignes « habituelle » des réglages resserrées, le sélecteur d'inertie se parcourt aux flèches (le cran actuel atteignable, jamais choisi), la marque « seed a minima » porte le vrai nombre de votes. Contrôles : C25 (ordre, dont la chaîne et le téléphone), C26 (titre), C27 (participation ramenée) ; C3, C17 (41 modules), C20 mis à jour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
import DocPanel from '$lib/components/DocPanel.svelte';
|
||||
import Guard from '$lib/components/Guard.svelte';
|
||||
import Icon from '$lib/components/Icon.svelte';
|
||||
import OrderModule from '$lib/components/OrderModule.svelte';
|
||||
import TitleBar from '$lib/components/TitleBar.svelte';
|
||||
import UnitBlock from '$lib/components/UnitBlock.svelte';
|
||||
import VoteFold from '$lib/components/VoteFold.svelte';
|
||||
import WaveRule from '$lib/components/WaveRule.svelte';
|
||||
@@ -28,7 +30,7 @@
|
||||
$effect(() => {
|
||||
const v = doc.view;
|
||||
if (!v) return;
|
||||
const known = new Set(sections(v.units).flatMap((s) => s.slots.flatMap((x) => [...x.versions, ...x.inertia].map((p) => p.id))));
|
||||
const known = new Set(sections(v.units, v.modules).flatMap((s) => s.slots.flatMap((x) => [...x.versions, ...x.inertia].map((p) => p.id))));
|
||||
basket.load(v.doc.id, known);
|
||||
});
|
||||
|
||||
@@ -76,7 +78,7 @@
|
||||
const sign = (lines: Line[]) => (signing = lines);
|
||||
// While a ballot waits for the chain, the page looks again every ten seconds.
|
||||
const waitingChain = $derived(
|
||||
(doc.view?.units ?? []).some((u) => [u.inForce, ...u.proposals].some((p) => p?.myBallot?.status === 'pending')),
|
||||
doc.ordered.some((u) => [u.inForce, ...u.proposals].some((p) => p?.myBallot?.status === 'pending')),
|
||||
);
|
||||
$effect(() => {
|
||||
if (!waitingChain) return;
|
||||
@@ -93,6 +95,10 @@
|
||||
// One line per article; an article stays when one of its versions passes the filter.
|
||||
const deep = $derived(articles(doc.open, filter));
|
||||
const rivalsOf = (u: Unit) => u.proposals.filter((p) => p.status === 'open' && p.kind === 'text').length;
|
||||
const nameOf = (id: string) => doc.articleTitle(id, lang, source);
|
||||
// The title of the document, a module (ADR 0009): its gestures under the title.
|
||||
const titleUnit = $derived(view?.modules.title ?? null);
|
||||
const titleStale = $derived(!!titleUnit?.inForce && shown(titleUnit.inForce, lang, source).stale);
|
||||
const VIEWS: View[] = ['inforce', 'wellrated', 'all'];
|
||||
|
||||
function onUnit(u: Unit, action: 'vote' | 'proposals' | 'propose', version: string | null = u.inForce?.id ?? null) {
|
||||
@@ -103,12 +109,14 @@
|
||||
openPanel({ unitId: u.id, proposalId: null, tab: action });
|
||||
}
|
||||
// The text shows the vote of its own versions; the list below, that of the others.
|
||||
// The modules in force are read in place too: the title at the head, each order at the end of its section.
|
||||
const textIds = $derived(
|
||||
new Set(
|
||||
doc.mode === 'inforce' || !doc.preview
|
||||
? doc.inForce.map((u) => u.inForce?.id)
|
||||
: doc.preview.map((pu) => pu.proposalId ?? doc.unit(pu.unitId)?.inForce?.id),
|
||||
),
|
||||
new Set([
|
||||
titleUnit?.inForce?.id,
|
||||
...(doc.mode === 'inforce' || !doc.preview
|
||||
? [...doc.inForce, ...(view?.modules.orders ?? [])].map((u) => u.inForce?.id)
|
||||
: doc.preview.map((pu) => pu.proposalId ?? doc.unit(pu.unitId)?.inForce?.id)),
|
||||
]),
|
||||
);
|
||||
const inText = $derived(!!vote && textIds.has(vote));
|
||||
const canVote = $derived(!!view?.vote.open);
|
||||
@@ -137,6 +145,18 @@
|
||||
<a href="/g/{view.doc.groupSlug}">{view.doc.groupName}</a><span class="sep" aria-hidden="true">·</span><a href="/g/{view.doc.groupSlug}/docs">{t('nav.docs')}</a>
|
||||
</p>
|
||||
<h1>{title}</h1>
|
||||
{#if titleUnit?.inForce}
|
||||
{@const tp = titleUnit.inForce}
|
||||
<TitleBar
|
||||
unit={titleUnit}
|
||||
stale={titleStale}
|
||||
selected={panel?.unitId === titleUnit.id}
|
||||
voteOpen={inText && vote === tp.id}
|
||||
onaction={(action) => onUnit(titleUnit, action)}
|
||||
>
|
||||
{#snippet fold()}{@render foldOf(titleUnit, tp)}{/snippet}
|
||||
</TitleBar>
|
||||
{/if}
|
||||
<p class="summary">{summary}</p>
|
||||
<p class="facts faint">
|
||||
{i18n.plural('docs.inForce', doc.inForce.length)} · {i18n.plural('docs.open', doc.open.length)}
|
||||
@@ -171,21 +191,37 @@
|
||||
|
||||
<section class="text" aria-label={t('doc.view.inforce')}>
|
||||
{#if doc.mode === 'inforce' || !doc.preview}
|
||||
{#each doc.inForce as u (u.id)}
|
||||
{#if u.inForce}
|
||||
{@const p = u.inForce}
|
||||
<UnitBlock
|
||||
text={shown(p, lang, source)}
|
||||
seedMinimal={p.seedMinimal}
|
||||
enteredAt={p.enteredAt}
|
||||
rivals={rivalsOf(u)}
|
||||
selected={panel?.unitId === u.id}
|
||||
voteFor={p.id}
|
||||
voteOpen={inText && vote === p.id}
|
||||
onaction={(action) => onUnit(u, action)}
|
||||
{#each doc.inForceBySection as s, i (`${i}:${s.section}`)}
|
||||
{#each s.units as u (u.id)}
|
||||
{#if u.inForce}
|
||||
{@const p = u.inForce}
|
||||
<UnitBlock
|
||||
text={shown(p, lang, source)}
|
||||
seedMinimal={p.seedMinimal}
|
||||
enteredAt={p.enteredAt}
|
||||
votes={p.tally.for + p.tally.against}
|
||||
rivals={rivalsOf(u)}
|
||||
selected={panel?.unitId === u.id}
|
||||
voteFor={p.id}
|
||||
voteOpen={inText && vote === p.id}
|
||||
onaction={(action) => onUnit(u, action)}
|
||||
>
|
||||
{#snippet fold()}{@render foldOf(u, p)}{/snippet}
|
||||
</UnitBlock>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if s.order?.inForce}
|
||||
{@const o = s.order}
|
||||
{@const op = s.order.inForce}
|
||||
<OrderModule
|
||||
unit={o}
|
||||
{nameOf}
|
||||
selected={panel?.unitId === o.id}
|
||||
voteOpen={inText && vote === op.id}
|
||||
onaction={(action) => onUnit(o, action)}
|
||||
>
|
||||
{#snippet fold()}{@render foldOf(u, p)}{/snippet}
|
||||
</UnitBlock>
|
||||
{#snippet fold()}{@render foldOf(o, op)}{/snippet}
|
||||
</OrderModule>
|
||||
{/if}
|
||||
{:else}
|
||||
<p class="muted">{t('doc.empty')}</p>
|
||||
@@ -239,6 +275,7 @@
|
||||
vote={inText ? null : vote}
|
||||
ontoggle={(_, p) => toggleVote(p.id)}
|
||||
fold={foldOf}
|
||||
{nameOf}
|
||||
onsection={canVote ? (section) => openPanel({ unitId: null, proposalId: null, tab: 'basket', section }) : undefined}
|
||||
/>
|
||||
<button class="btn new" type="button" onclick={() => openPanel({ unitId: null, proposalId: null, tab: 'propose' })}><Icon name="plus" size={16} />{t('deep.new')}</button>
|
||||
|
||||
Reference in New Issue
Block a user