forked from yvv/decision
Lot U2 : le lecteur — cibles de 44 px sur téléphone dans tout le vote déplié et « Tout déplier » ; les trois lectures passent à la ligne au lieu de cacher « Si tout passe » ; « Proposer cet ordre » éteint dit pourquoi, à côté ; l'inertie dite une fois (ses crans dans « Inertie »), la ligne d'une Proposition seed s'efface sous son vote déplié, les versions déjà listées ne sont pas redites dans « Autres versions » ; l'en-tête compte ce que montre la liste (les Propositions seed à part) ; un changement d'inertie d'un ordre ou du titre a sa pastille sur la ligne du module (fr, en, es)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,8 @@ test('C25 — l’ordre des articles : une ligne en fin de section, réordonnée
|
||||
await fond.hover();
|
||||
await fond.getByRole('button', { name: 'Le vote' }).click();
|
||||
const fondVote = page.getByRole('region', { name: 'Le vote de « Ordre des articles · Engagements fondamentaux »' });
|
||||
await expect(fondVote.getByText('inertie 2 / 5 · basse', { exact: true })).toBeVisible();
|
||||
// Its inertia, said once, in its own place (lot U2).
|
||||
await expect(fondVote.getByRole('heading', { name: 'Inertie · 2 / 5 · basse' })).toBeVisible();
|
||||
await expect(fondVote.getByRole('list', { name: 'Ordre en vigueur' }).locator('li')).toHaveText(['Engagements fondamentaux', 'Unicité du compte', 'Réciprocité']);
|
||||
await fond.getByRole('button', { name: 'Le vote' }).click();
|
||||
|
||||
@@ -171,7 +172,7 @@ test('C26 — le titre du document : proposé et voté en tête, ses traductions
|
||||
// In force now: its vote stays unfolded, under the title.
|
||||
const vote = bar.locator('section.fold');
|
||||
await expect(bar.getByRole('button', { name: 'Le vote' })).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(vote.getByText('inertie 4 / 5 · haute', { exact: true })).toBeVisible();
|
||||
await expect(vote.getByRole('heading', { name: 'Inertie · 4 / 5 · haute' })).toBeVisible();
|
||||
await expect(vote.getByText(`${voices} pour · 0 contre`)).toBeVisible();
|
||||
await shot(page, 'c26-2-titre-vote');
|
||||
await bar.getByRole('button', { name: 'Le vote' }).click();
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
// (ADR 0007, « cinq crans »). The water level of a vote stands upright; the
|
||||
// weight of a text lies flat — two shapes one does not confuse. The words stay
|
||||
// beside it: « inertie 3 / 5 · moyenne », or only « moyenne » when `short`
|
||||
// (a screen reader still hears the whole).
|
||||
// (a screen reader still hears the whole), or « 3 / 5 · moyenne » when `bare`,
|
||||
// after a heading that already says « Inertie ».
|
||||
import { t } from '$lib/i18n/index.svelte';
|
||||
import { inertiaBadge } from '$lib/labels';
|
||||
import { inertiaBadge, inertiaLevel } from '$lib/labels';
|
||||
import type { Inertia } from '$lib/types';
|
||||
|
||||
let { level, short = false }: { level: Inertia; short?: boolean } = $props();
|
||||
let { level, short = false, bare = false }: { level: Inertia; short?: boolean; bare?: boolean } = $props();
|
||||
const full = $derived(inertiaBadge(level));
|
||||
</script>
|
||||
|
||||
@@ -16,7 +17,9 @@
|
||||
<span class="notches" aria-hidden="true">
|
||||
{#each [1, 2, 3, 4, 5] as k (k)}<span class="notch" class:on={k <= level}></span>{/each}
|
||||
</span>
|
||||
{#if short}<span class="visually-hidden">{full}</span><span aria-hidden="true">{t(`inertia.name.${level}`)}</span>{:else}<span>{full}</span>{/if}
|
||||
{#if short}<span class="visually-hidden">{full}</span><span aria-hidden="true">{t(`inertia.name.${level}`)}</span>
|
||||
{:else if bare}<span>{inertiaLevel(level)}</span>
|
||||
{:else}<span>{full}</span>{/if}
|
||||
</span>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
/** Every section open (a filter is on). */
|
||||
all?: boolean;
|
||||
ontoggle: (unit: Unit, p: Proposal) => void;
|
||||
fold: Snippet<[Unit, Proposal]>;
|
||||
/** The vote of a version, and the versions already listed beside it (not named again inside). */
|
||||
fold: Snippet<[Unit, Proposal, string[]]>;
|
||||
/** The order line of a section, at its end. */
|
||||
order: Snippet<[Unit]>;
|
||||
/** Opens the basket on a section; absent when this session does not vote here. */
|
||||
@@ -153,7 +154,7 @@
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{#if vote === best.id}<div class="fold">{@render fold(unit, best)}</div>{/if}
|
||||
{#if vote === best.id}<div class="fold">{@render fold(unit, best, [])}</div>{/if}
|
||||
</li>
|
||||
{:else if best}
|
||||
{@const vopen = isVersionsOpen(a)}
|
||||
@@ -194,7 +195,7 @@
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{#if vote === p.id}{@render fold(unit, p)}{/if}
|
||||
{#if vote === p.id}{@render fold(unit, p, a.versions.map((v) => v.id))}{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
@@ -431,7 +432,8 @@
|
||||
}
|
||||
}
|
||||
@media (max-width: 899px) {
|
||||
.vote-section {
|
||||
.vote-section,
|
||||
.linkish {
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,15 +70,17 @@
|
||||
min-width: 0;
|
||||
border-radius: var(--r-input);
|
||||
background: var(--surface-2);
|
||||
padding: 0.7rem 0.9rem;
|
||||
padding: 0 0.9rem;
|
||||
}
|
||||
/* The whole height of the closed box is the summary: a target a finger finds (lot U2). */
|
||||
summary {
|
||||
cursor: pointer;
|
||||
font-weight: 750;
|
||||
color: var(--ink-soft);
|
||||
padding-block: 0.7rem;
|
||||
}
|
||||
.formula[open] summary {
|
||||
margin-bottom: 0.7rem;
|
||||
.formula[open] {
|
||||
padding-bottom: 0.7rem;
|
||||
}
|
||||
.formula > :not(summary) + :not(summary) {
|
||||
margin-top: 0.6rem;
|
||||
@@ -112,4 +114,9 @@
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
@media (max-width: 899px) {
|
||||
.settings-link {
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
// The inertia of an article: its level out of five, its past, the changes
|
||||
// under vote, and the choice of a new level. A change of inertia is voted at
|
||||
// the level of the article itself (ADR 0007).
|
||||
// The inertia of an article: its level out of five — drawn, said once in the vote (lot
|
||||
// U2) —, its past, the changes under vote, and the choice of a new level. A change of
|
||||
// inertia is voted at the level of the article itself (ADR 0007).
|
||||
import { api, ApiError } from '$lib/api';
|
||||
import type { DocState } from '$lib/doc.svelte';
|
||||
import { i18n, t, type Key } from '$lib/i18n/index.svelte';
|
||||
import { inertiaLevel } from '$lib/labels';
|
||||
import { INERTIA_LEVELS, type Inertia, type Unit } from '$lib/types';
|
||||
import Crans from './Crans.svelte';
|
||||
|
||||
let { doc, unit, onopen }: { doc: DocState; unit: Unit; onopen: (proposalId: string) => void } = $props();
|
||||
|
||||
@@ -59,7 +60,7 @@
|
||||
</script>
|
||||
|
||||
<section class="inertia" aria-labelledby="inertia-{unit.id}">
|
||||
<h4 id="inertia-{unit.id}">{t('inertia.title')} · <span class="muted">{inertiaLevel(unit.inertia)}</span></h4>
|
||||
<h4 id="inertia-{unit.id}">{t('inertia.title')} · <span class="muted"><Crans level={unit.inertia} bare /></span></h4>
|
||||
<p class="faint small">{t('inertia.explain')}</p>
|
||||
{#each unit.inertiaChanges as c (c.at + c.to)}
|
||||
<p class="faint small">{t(`inertia.history.${c.cause}`, { from: level(c.from), to: level(c.to), date: i18n.date(c.at) })}</p>
|
||||
@@ -195,6 +196,19 @@
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
/* On a phone: every gesture big enough for a finger (lot U2). */
|
||||
@media (max-width: 899px) {
|
||||
details > summary {
|
||||
padding-block: 0.75rem;
|
||||
}
|
||||
details[open] > summary {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.btn,
|
||||
.link {
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.levels button {
|
||||
transition: none;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// The order of a section, at its end (ADR 0009; Yvv, 2026-11-02: « la répétition de
|
||||
// "ordonnancement" est nulle. Il faudrait que ce soit en fin de section »): one line, never
|
||||
// the list of its articles again — the handle that marks an order, « Ordre de la section »,
|
||||
// its weight in notches, its state, the orders proposed. « Le vote » unfolds here the vote
|
||||
// its weight in notches, its state, the orders proposed and the changes of its inertia
|
||||
// proposed (lot U2: found here, where their vote unfolds). « Le vote » unfolds here the vote
|
||||
// of any of its versions (the list whole); « Réordonner » puts handles on the articles of
|
||||
// the section, in place.
|
||||
import type { Snippet } from 'svelte';
|
||||
@@ -37,6 +38,7 @@
|
||||
|
||||
const p = $derived(unit.inForce);
|
||||
const proposed = $derived(unit.proposals.filter((x) => x.status === 'open' && x.kind === 'order'));
|
||||
const inertia = $derived(unit.proposals.filter((x) => x.status === 'open' && x.kind === 'inertia'));
|
||||
// Any version of the module unfolds here: its order in force, an order proposed, a change of its inertia.
|
||||
const shown = $derived([p, ...unit.proposals.filter((x) => x.status === 'open')].find((x) => x?.id === vote) ?? null);
|
||||
const votes = $derived((p?.tally.for ?? 0) + (p?.tally.against ?? 0));
|
||||
@@ -53,6 +55,10 @@
|
||||
{@const first = proposed[0]}
|
||||
<button type="button" class="proposed" onclick={() => first && onvote(first.id)}>{i18n.plural('order.proposed', proposed.length)}</button>
|
||||
{/if}
|
||||
{#if inertia.length && !reordering}
|
||||
{@const first = inertia[0]}
|
||||
<button type="button" class="proposed" onclick={() => first && onvote(first.id)}>{i18n.plural('module.inertia.proposed', inertia.length)}</button>
|
||||
{/if}
|
||||
{#if !reordering}
|
||||
<span class="links">
|
||||
{#if p}<button type="button" class:on={shown?.id === p.id} aria-expanded={shown?.id === p.id} aria-controls="vote-{p.id}" onclick={() => onvote(p.id)}>{t('doc.action.vote')}</button>{/if}
|
||||
|
||||
@@ -110,4 +110,10 @@
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
/* On a phone: « Fixer ce quorum » and « Revenir au défaut » as tall as the field (lot U2). */
|
||||
@media (max-width: 899px) {
|
||||
.btn {
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
// or uses its arrows; with the keyboard, the up and down arrows move the article that has
|
||||
// the focus, and the new place is said. Then « Proposer cet ordre »: the order goes to the
|
||||
// vote (ADR 0009, `POST /units/{id}/order`) and its vote unfolds at once, here. An order
|
||||
// already proposed is voted, never proposed twice.
|
||||
// already proposed is voted, never proposed twice. While the order is the one in force,
|
||||
// the button stays off and says why, beside it (lot U2, UX-lecteur.md).
|
||||
import { tick, untrack } from 'svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { api } from '$lib/api';
|
||||
@@ -141,10 +142,7 @@
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (unchanged) {
|
||||
error = t('order.edit.unchanged');
|
||||
return;
|
||||
}
|
||||
if (unchanged) return;
|
||||
if (existing) {
|
||||
ondone(existing.id);
|
||||
return;
|
||||
@@ -192,8 +190,15 @@
|
||||
{#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">
|
||||
{#if unchanged}<p class="why" id="order-why-{unit.id}">{t('order.edit.unchanged')}</p>{/if}
|
||||
<button class="btn quiet small" type="button" disabled={busy} onclick={oncancel}>{t('common.cancel')}</button>
|
||||
<button class="btn primary small" type="button" disabled={busy || unchanged} onclick={submit}>{t(existing ? 'order.edit.vote' : 'order.edit.submit')}</button>
|
||||
<button
|
||||
class="btn primary small"
|
||||
type="button"
|
||||
disabled={busy || unchanged}
|
||||
aria-describedby={unchanged ? `order-why-${unit.id}` : undefined}
|
||||
onclick={submit}>{t(existing ? 'order.edit.vote' : 'order.edit.submit')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -338,15 +343,31 @@
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
/* Why the button is off, on its left: read before one reaches for it. */
|
||||
.why {
|
||||
flex: 1 1 14rem;
|
||||
margin: 0;
|
||||
font-size: 0.84rem;
|
||||
font-weight: 650;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
/* On a phone: every target is big enough for a finger. */
|
||||
@media (max-width: 899px) {
|
||||
.arrow {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
.actions .btn {
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
/* Its own line, above the two buttons kept together. */
|
||||
.why {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
@keyframes unfold {
|
||||
from {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
// The title of the document is a module (ADR 0009, inertia 4): one line under the title,
|
||||
// of the same voice as the order of a section — its weight in notches, its state, the
|
||||
// titles proposed. « Le vote » unfolds here the vote of any of its versions; another title
|
||||
// is written in the panel.
|
||||
// titles proposed and the changes of its inertia proposed (lot U2). « Le vote » unfolds here
|
||||
// the vote of any of its versions; another title is written in the panel.
|
||||
import type { Snippet } from 'svelte';
|
||||
import { i18n, t } from '$lib/i18n/index.svelte';
|
||||
import { moduleName } from '$lib/labels';
|
||||
@@ -33,6 +33,7 @@
|
||||
const p = $derived(unit.inForce);
|
||||
const votes = $derived((p?.tally.for ?? 0) + (p?.tally.against ?? 0));
|
||||
const proposed = $derived(unit.proposals.filter((x) => x.status === 'open' && x.kind === 'title'));
|
||||
const inertia = $derived(unit.proposals.filter((x) => x.status === 'open' && x.kind === 'inertia'));
|
||||
const shown = $derived([p, ...unit.proposals.filter((x) => x.status === 'open')].find((x) => x?.id === vote) ?? null);
|
||||
</script>
|
||||
|
||||
@@ -49,6 +50,10 @@
|
||||
{@const first = proposed[0]}
|
||||
<button type="button" class="proposed" onclick={() => first && onvote(first.id)}>{i18n.plural('title.proposed', proposed.length)}</button>
|
||||
{/if}
|
||||
{#if inertia.length}
|
||||
{@const first = inertia[0]}
|
||||
<button type="button" class="proposed" onclick={() => first && onvote(first.id)}>{i18n.plural('module.inertia.proposed', inertia.length)}</button>
|
||||
{/if}
|
||||
<span class="links">
|
||||
{#if p}<button type="button" class:on={shown?.id === p.id} aria-expanded={shown?.id === p.id} aria-controls="vote-{p.id}" onclick={() => onvote(p.id)}>{t('doc.action.vote')}</button>{/if}
|
||||
<button type="button" onclick={onpropose}>{t('title.propose')}</button>
|
||||
|
||||
@@ -67,15 +67,19 @@
|
||||
<Prose text={text.body} />
|
||||
{#if actions}
|
||||
<footer class="actions">
|
||||
<span class="status faint">
|
||||
{#if seed}
|
||||
{@const need = needOf(seed)}
|
||||
{t('tally.short', { for: seed.tally.for, against: seed.tally.against })}{#if need}{' · '}<span title={need.title}>{need.short}</span>{/if}
|
||||
<span class="crans"><Crans level={seed.votedAtInertia} short /></span>
|
||||
{:else if seedMinimal}<span title={t('doc.seed.long')}>{i18n.plural('doc.seed.votes', votes)}</span>
|
||||
{:else if enteredAt}{t('doc.adopted', { date: i18n.date(enteredAt) })}{/if}
|
||||
{#if rivals > 0} · {i18n.plural('doc.others', rivals)}{/if}
|
||||
</span>
|
||||
<!-- Its vote unfolded says the totals, the « oui » to pass, the inertia and the other
|
||||
versions: the line does not say them again (lot U2). -->
|
||||
{#if !(seed && voteOpen)}
|
||||
<span class="status faint">
|
||||
{#if seed}
|
||||
{@const need = needOf(seed)}
|
||||
{t('tally.short', { for: seed.tally.for, against: seed.tally.against })}{#if need}{' · '}<span title={need.title}>{need.short}</span>{/if}
|
||||
<span class="crans"><Crans level={seed.votedAtInertia} short /></span>
|
||||
{:else if seedMinimal}<span title={t('doc.seed.long')}>{i18n.plural('doc.seed.votes', votes)}</span>
|
||||
{:else if enteredAt}{t('doc.adopted', { date: i18n.date(enteredAt) })}{/if}
|
||||
{#if rivals > 0 && !voteOpen}{' · '}{i18n.plural('doc.others', rivals)}{/if}
|
||||
</span>
|
||||
{/if}
|
||||
<span class="links">
|
||||
<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('propose')}>{proposeLabel ?? t('doc.action.propose')}</button>
|
||||
@@ -129,11 +133,13 @@
|
||||
.crans {
|
||||
margin-left: 0.6rem;
|
||||
}
|
||||
/* The gestures keep their place whether the line of state is there or not (it steps aside
|
||||
while the vote is unfolded, lot U2): on the right on a desktop, on the left on a phone. */
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
gap: 0.4rem 1rem;
|
||||
margin-top: 0.7rem;
|
||||
min-height: 2.25rem;
|
||||
@@ -170,6 +176,11 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
.links {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
@media (max-width: 899px) {
|
||||
.links button {
|
||||
min-height: 2.75rem;
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
// many « oui » pass it, how many « non » block it (ADR 0013), its weight in notches,
|
||||
// its other versions — each opens its own vote where it reads —, and the quorum its
|
||||
// author may set before the first vote. The rest of a version (typos, translations,
|
||||
// objections, history) lives in the panel, « Détails ».
|
||||
// objections, history) lives in the panel, « Détails ». Lot U2: each thing said once —
|
||||
// the inertia in its own place, the other versions only those not already in sight.
|
||||
import { onMount } from 'svelte';
|
||||
import { api } from '$lib/api';
|
||||
import type { DocState } from '$lib/doc.svelte';
|
||||
@@ -31,6 +32,7 @@
|
||||
doc,
|
||||
proposal: p,
|
||||
unit,
|
||||
besides = [],
|
||||
onopen,
|
||||
onsettings,
|
||||
ondetails,
|
||||
@@ -40,6 +42,8 @@
|
||||
doc: DocState;
|
||||
proposal: Proposal;
|
||||
unit: Unit;
|
||||
/** The versions already in sight beside this vote (the versions of its article, listed): not named again. */
|
||||
besides?: string[];
|
||||
/** Unfolds the vote of another version, where it reads (another text, a change of inertia). */
|
||||
onopen: (proposalId: string) => void;
|
||||
onsettings: (proposalId: string) => void;
|
||||
@@ -80,12 +84,17 @@
|
||||
const nameOf = (id: string) => doc.articleTitle(id, doc.original ? source : i18n.lang, source);
|
||||
/** « 9 oui pour passer · 4 non pour bloquer » — the quorum in its tooltip. */
|
||||
const need = $derived(needOf(p));
|
||||
/** The other live versions of the module (its changes of inertia live with its inertia). */
|
||||
/**
|
||||
* The other live versions of the module (its changes of inertia live with its inertia),
|
||||
* less those already in sight beside the vote.
|
||||
*/
|
||||
const others = $derived(
|
||||
[unit.inForce, ...unit.proposals.filter((x) => x.status === 'open' && x.kind !== 'inertia')].filter(
|
||||
(x): x is Proposal => !!x && x.id !== p.id,
|
||||
(x): x is Proposal => !!x && x.id !== p.id && !besides.includes(x.id),
|
||||
),
|
||||
);
|
||||
/** The inertia is said by « Inertie », below; a change of inertia has none: its level is said here. */
|
||||
const ownInertia = $derived(p.kind === 'inertia');
|
||||
|
||||
/** What tells another version apart, in a few words. */
|
||||
function otherLabel(v: Proposal): string {
|
||||
@@ -165,7 +174,7 @@
|
||||
{#if need}<p class="need" title={need.title}>{need.long}</p>{/if}
|
||||
<p class="state">{p.evaluation.reached ? t('tally.reached') : i18n.plural('tally.missing', p.evaluation.missing)}</p>
|
||||
{/if}
|
||||
<p class="faint small"><Crans level={p.votedAtInertia} /></p>
|
||||
{#if ownInertia}<p class="faint small"><Crans level={p.votedAtInertia} /></p>{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if p.blocked}
|
||||
@@ -277,7 +286,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
{/if}
|
||||
{#if p.kind !== 'inertia'}<InertiaPicker {doc} {unit} {onopen} />{/if}
|
||||
{#if !ownInertia}<InertiaPicker {doc} {unit} {onopen} />{/if}
|
||||
{#if !unit.kind && unit.canSetQuorum}<QuorumSetting {doc} {unit} />{/if}
|
||||
{#if detail && view}
|
||||
<Formula
|
||||
@@ -504,6 +513,14 @@
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
/* On a phone: every gesture of the vote big enough for a finger (lot U2). */
|
||||
@media (max-width: 899px) {
|
||||
.choice .btn,
|
||||
.disclose,
|
||||
.linkish {
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
}
|
||||
@keyframes unfold {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
||||
@@ -103,6 +103,8 @@ export const en: Messages = {
|
||||
'docs.inForce.other': '{n} articles in force',
|
||||
'docs.open.one': '{n} open proposal',
|
||||
'docs.open.other': '{n} open proposals',
|
||||
'docs.seeds.one': '{n} seed proposal',
|
||||
'docs.seeds.other': '{n} seed proposals',
|
||||
|
||||
'doc.view.label': 'Read',
|
||||
'doc.view.inforce': 'In force',
|
||||
@@ -207,6 +209,8 @@ export const en: Messages = {
|
||||
'title.submitted': 'Title submitted to vote.',
|
||||
'title.proposed.one': '{n} title proposed',
|
||||
'title.proposed.other': '{n} titles proposed',
|
||||
'module.inertia.proposed.one': '{n} inertia change proposed',
|
||||
'module.inertia.proposed.other': '{n} inertia changes proposed',
|
||||
|
||||
'tally.short': '{for} for · {against} against',
|
||||
'tally.missing.one': '{n} more vote in favour needed.',
|
||||
|
||||
@@ -103,6 +103,8 @@ export const es: Messages = {
|
||||
'docs.inForce.other': '{n} artículos en vigor',
|
||||
'docs.open.one': '{n} propuesta en curso',
|
||||
'docs.open.other': '{n} propuestas en curso',
|
||||
'docs.seeds.one': '{n} propuesta semilla',
|
||||
'docs.seeds.other': '{n} propuestas semilla',
|
||||
|
||||
'doc.view.label': 'Leer',
|
||||
'doc.view.inforce': 'En vigor',
|
||||
@@ -207,6 +209,8 @@ export const es: Messages = {
|
||||
'title.submitted': 'Título sometido a votación.',
|
||||
'title.proposed.one': '{n} título propuesto',
|
||||
'title.proposed.other': '{n} títulos propuestos',
|
||||
'module.inertia.proposed.one': '{n} cambio de inercia propuesto',
|
||||
'module.inertia.proposed.other': '{n} cambios de inercia propuestos',
|
||||
|
||||
'tally.short': '{for} a favor · {against} en contra',
|
||||
'tally.missing.one': 'Falta {n} voto a favor.',
|
||||
|
||||
@@ -107,6 +107,8 @@ export const fr = {
|
||||
'docs.inForce.other': '{n} articles en vigueur',
|
||||
'docs.open.one': '{n} proposition en cours',
|
||||
'docs.open.other': '{n} propositions en cours',
|
||||
'docs.seeds.one': '{n} Proposition seed',
|
||||
'docs.seeds.other': '{n} Propositions seed',
|
||||
|
||||
'doc.view.label': 'Lire',
|
||||
'doc.view.inforce': 'En vigueur',
|
||||
@@ -211,6 +213,8 @@ export const fr = {
|
||||
'title.submitted': 'Titre proposé au vote.',
|
||||
'title.proposed.one': '{n} titre proposé',
|
||||
'title.proposed.other': '{n} titres proposés',
|
||||
'module.inertia.proposed.one': "{n} changement d'inertie proposé",
|
||||
'module.inertia.proposed.other': "{n} changements d'inertie proposés",
|
||||
|
||||
'tally.short': '{for} pour · {against} contre',
|
||||
'tally.missing.one': 'Il manque {n} voix pour.',
|
||||
|
||||
@@ -153,6 +153,11 @@
|
||||
const sectionKeys = $derived([...new Set((view?.units ?? []).map((u) => u.section))]);
|
||||
const deepSecs = $derived(deepSections(deep, deepOrders, sectionKeys));
|
||||
const canVote = $derived(!!view?.vote.open);
|
||||
// The facts of the head say what the page shows (lot U2): the seed proposals read at the head
|
||||
// while nothing is in force, apart; then the proposals of « Propositions en cours » — the
|
||||
// articles' versions, never the orders and titles proposed, voted at their module's line.
|
||||
const headSeeds = $derived(doc.seedHead.length);
|
||||
const openArticles = $derived(doc.open.filter((x) => !x.unit.kind).length - headSeeds);
|
||||
|
||||
/** The other versions of an article open to the vote, besides the one read. */
|
||||
const othersOf = (e: Entry) => e.unit.proposals.filter((p) => p.status === 'open' && p.kind !== 'inertia' && p.id !== e.proposal.id).length;
|
||||
@@ -164,11 +169,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet foldOf(unit: Unit, p: Proposal)}
|
||||
{#snippet foldOf(unit: Unit, p: Proposal, besides: string[] = [])}
|
||||
<VoteFold
|
||||
{doc}
|
||||
proposal={p}
|
||||
{unit}
|
||||
{besides}
|
||||
onopen={(id) => go(panel, id)}
|
||||
onsettings={(id) => openPanel({ unitId: unit.id, proposalId: id, tab: 'settings' })}
|
||||
ondetails={(id) => openPanel({ unitId: unit.id, proposalId: id, tab: 'details' })}
|
||||
@@ -223,7 +229,7 @@
|
||||
{/if}
|
||||
<p class="summary">{summary}</p>
|
||||
<p class="facts faint">
|
||||
{i18n.plural('docs.inForce', doc.inForce.length)} · {i18n.plural('docs.open', doc.open.length)}
|
||||
{i18n.plural('docs.inForce', doc.inForce.length)} · {#if headSeeds}{i18n.plural('docs.seeds', headSeeds)}{' · '}{/if}{i18n.plural('docs.open', openArticles)}
|
||||
{#if view.doc.publication.state === 'done'} · {t('doc.published')}{:else if view.doc.publication.state === 'pending'} · {t('doc.publish.pending')}{/if}
|
||||
· <button class="linkish" type="button" onclick={() => openPanel({ unitId: null, proposalId: null, tab: 'settings' })}>{t('settings.title')}</button>
|
||||
</p>
|
||||
@@ -517,13 +523,15 @@
|
||||
.new {
|
||||
margin-top: 1.4rem;
|
||||
}
|
||||
/* On a phone: the three readings on one line that scrolls, the targets big enough for a finger. */
|
||||
/* On a phone: the three readings go to the line rather than hide « Si tout passe » off the
|
||||
edge (lot U2); each row fills the width, the targets big enough for a finger. */
|
||||
@media (max-width: 899px) {
|
||||
.seg.views {
|
||||
flex-wrap: nowrap;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
.seg.views button {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.seg button {
|
||||
min-height: 2.75rem;
|
||||
|
||||
Reference in New Issue
Block a user