- Aujourd'hui (Fil 13 sections + capture sticky) + Le chemin (tunnel 2 gestes, Q0 inline, 3 chips, dérogation asymétrique, alternatives réglage/consignation) - Registre + fiche décision (timeline, périmètre premier/second lieu auditable, affluence non-ignorable, S'instruire condensé, éléments + cartographie de clôture, épreuve du réel, Remettre en question, PV A4, gravure) - Salle de vote 5 modalités (consentement, nuancé+histogramme, binaire hérité avec jauge inertielle, Réglage collectif complet — faisceau, médiane basse, Pour moi, Explorer, cristallisation-geste —, élection à départage humain) - Textes (Pacte en clair, document vivant, diff, vue projetée, Atelier des formules porté du v1 sur le moteur unique) + Mandats (faits comptés, feux de la rampe, wizard 3 étapes) + Observatoire (consigner→observer→protocoliser) - Onboarding 7 gabarits + Données locales (export/import, attributs, atelier) - voting→framing gardé (Reformuler d'un réglage figé non cristallisé) - Pages v1 supprimées (login, documents, mandates, protocols, sanctuary, tools, decisions/new) — 342 tests verts, build zéro erreur Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
164 lines
4.7 KiB
Vue
164 lines
4.7 KiB
Vue
<script setup lang="ts">
|
|
// <!-- ld-v2 --> Mini-démo interactive du Réglage collectif : cinq curseurs-votes
|
|
// jouets, la médiane basse recalculée en direct par ~/engine (medianByElement),
|
|
// et la règle de cristallisation — l'agrégat éclaire, le geste cristallise.
|
|
import { medianByElement } from '~/engine'
|
|
import { CRYSTALLIZE_ACTION, MEDIAN_EXPLANATION } from '~/lexicon'
|
|
|
|
const TOY_VOTERS = ['Ana', 'Brice', 'Chloé', 'Dado', 'Elsa'] as const
|
|
|
|
const votes = ref<number[]>([20, 35, 40, 65, 80])
|
|
|
|
const median = computed(() => {
|
|
const result = medianByElement(votes.value.map(v => [v]))
|
|
return result[0] ?? 0
|
|
})
|
|
|
|
/** La médiane est toujours une valeur réellement votée — on la nomme. */
|
|
const medianVoters = computed(() =>
|
|
TOY_VOTERS.filter((_, i) => votes.value[i] === median.value))
|
|
</script>
|
|
|
|
<template>
|
|
<!-- ld-v2 -->
|
|
<div class="median-demo">
|
|
<div class="median-demo__sliders">
|
|
<label v-for="(name, i) in TOY_VOTERS" :key="name" class="median-demo__vote">
|
|
<span class="median-demo__voter">{{ name }}</span>
|
|
<input
|
|
v-model.number="votes[i]"
|
|
type="range"
|
|
min="0"
|
|
max="100"
|
|
step="5"
|
|
:aria-label="`Curseur-vote de ${name}`"
|
|
>
|
|
<span class="median-demo__value">{{ votes[i] }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Le faisceau : chaque point est un vote, le trait épais est la médiane -->
|
|
<div class="median-demo__strip" aria-hidden="true">
|
|
<span
|
|
v-for="(vote, i) in votes"
|
|
:key="i"
|
|
class="median-demo__dot"
|
|
:style="{ left: `${vote}%` }"
|
|
/>
|
|
<span class="median-demo__median-mark" :style="{ left: `${median}%` }" />
|
|
</div>
|
|
|
|
<p class="median-demo__readout">
|
|
Médiane basse : <strong>{{ median }}</strong>
|
|
<span v-if="medianVoters.length" class="median-demo__who">
|
|
— c'est la position de {{ medianVoters.join(' et ') }}
|
|
</span>
|
|
</p>
|
|
|
|
<p class="median-demo__explain">{{ MEDIAN_EXPLANATION }}</p>
|
|
|
|
<div class="median-demo__rule">
|
|
<UIcon name="i-lucide-stamp" class="median-demo__rule-icon" />
|
|
<div>
|
|
<p class="median-demo__rule-title">L'agrégat éclaire, le geste cristallise.</p>
|
|
<p class="median-demo__rule-body">
|
|
À la clôture, les votes sont figés et la médiane s'affiche — mais rien n'est
|
|
adopté tant qu'un·e garant·e n'a pas fait le geste « {{ CRYSTALLIZE_ACTION }} »,
|
|
daté et signé. Jamais d'adoption automatique.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.median-demo { display: flex; flex-direction: column; gap: 0.9rem; }
|
|
|
|
.median-demo__sliders { display: flex; flex-direction: column; gap: 0.45rem; }
|
|
.median-demo__vote {
|
|
display: grid;
|
|
grid-template-columns: 3.4rem 1fr 2.4rem;
|
|
align-items: center;
|
|
gap: 0.6rem;
|
|
}
|
|
.median-demo__voter { font-size: 0.8125rem; font-weight: 700; color: var(--mood-text-muted); }
|
|
.median-demo__vote input[type='range'] {
|
|
width: 100%;
|
|
accent-color: var(--mood-tertiary);
|
|
background: transparent;
|
|
min-height: 1.3rem;
|
|
}
|
|
.median-demo__value {
|
|
font-size: 0.8125rem;
|
|
font-weight: 700;
|
|
font-variant-numeric: tabular-nums;
|
|
text-align: right;
|
|
color: var(--mood-text);
|
|
}
|
|
|
|
.median-demo__strip {
|
|
position: relative;
|
|
height: 2rem;
|
|
margin: 0 0.3rem;
|
|
border-radius: var(--r-input);
|
|
background: color-mix(in srgb, var(--mood-text) 5%, transparent);
|
|
}
|
|
.median-demo__dot {
|
|
position: absolute;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background: var(--mood-tertiary);
|
|
opacity: 0.55;
|
|
transition: left 0.15s ease;
|
|
}
|
|
.median-demo__median-mark {
|
|
position: absolute;
|
|
top: 3px;
|
|
bottom: 3px;
|
|
transform: translateX(-50%);
|
|
width: 4px;
|
|
border-radius: 2px;
|
|
background: var(--mood-accent);
|
|
transition: left 0.15s ease;
|
|
}
|
|
|
|
.median-demo__readout { font-size: 0.9375rem; color: var(--mood-text); }
|
|
.median-demo__readout strong {
|
|
color: var(--mood-accent);
|
|
font-size: 1.1rem;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.median-demo__who { font-size: 0.8125rem; color: var(--mood-text-muted); }
|
|
|
|
.median-demo__explain {
|
|
font-size: 0.8125rem;
|
|
line-height: 1.6;
|
|
color: var(--mood-text-muted);
|
|
}
|
|
|
|
.median-demo__rule {
|
|
display: flex;
|
|
gap: 0.7rem;
|
|
align-items: flex-start;
|
|
padding: 0.8rem 0.95rem;
|
|
border-radius: var(--r-input);
|
|
background: var(--mood-accent-soft);
|
|
}
|
|
.median-demo__rule-icon {
|
|
font-size: 1.2rem;
|
|
color: var(--mood-accent);
|
|
margin-top: 2px;
|
|
flex-shrink: 0;
|
|
}
|
|
.median-demo__rule-title { font-size: 0.875rem; font-weight: 800; color: var(--mood-text); }
|
|
.median-demo__rule-body {
|
|
font-size: 0.8125rem;
|
|
line-height: 1.55;
|
|
color: var(--mood-text-muted);
|
|
margin-top: 2px;
|
|
}
|
|
</style>
|