v3 : socle Rust (moteur pur, accès trustinWallet, serveur) ; maquette Nuxt rangée dans maquette/
- crates/ld-engine : formule inertielle (parité avec la maquette, garde C), coquille / changement de fond, assistant d'édition à règles, composition d'un document sous vote permanent (adoptions, aperçus, rivales). - crates/trustin-access : qui voit, qui ouvre, qui gère les accès. - crates/ld-server : API axum + SQLite, connexion Ğ1 (client OAuth2 de connect.monnaie-libre.fr), invitations, entrée en vigueur automatique, signalements de contradiction, traductions éditoriales, publication git, taille de la toile lue sur la chaîne. - seeds/acte-engagement-certification.json : 33 articles, fr + en + es ; en vigueur : préambule, E1, E2 (seed a minima). - maquette/ : frontend, backend et docker v2 déplacés tels quels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
// <!-- ld-v2 --> Stacked initials avatars — deterministic tint from id, -8px overlap.
|
||||
// origin glyphs: 'computed' = full dot (concerné·e en premier lieu),
|
||||
// 'declared' = raised hand (en second lieu). Tap → parent shows inclusion reason.
|
||||
import type { Person } from '~/types/domain'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
people: { person: Person; origin?: 'computed' | 'declared'; reason?: string }[]
|
||||
max?: number
|
||||
size?: number
|
||||
}>(), { max: 6, size: 30 })
|
||||
|
||||
const emit = defineEmits<{ (e: 'tap', entry: { person: Person; origin?: string; reason?: string }): void }>()
|
||||
|
||||
const visible = computed(() => props.people.slice(0, props.max))
|
||||
const overflow = computed(() => Math.max(0, props.people.length - props.max))
|
||||
|
||||
function initials(name: string): string {
|
||||
return name.split(/[\s-]+/).map(w => w[0] ?? '').join('').slice(0, 2).toUpperCase()
|
||||
}
|
||||
function tint(id: string): string {
|
||||
let h = 0
|
||||
for (let i = 0; i < id.length; i++) h = (h * 31 + id.charCodeAt(i)) % 360
|
||||
return `oklch(0.72 0.09 ${h})`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- ld-v2 -->
|
||||
<div class="ld-avatars" :style="{ '--av-size': size + 'px' }">
|
||||
<button
|
||||
v-for="entry in visible"
|
||||
:key="entry.person.id"
|
||||
class="ld-avatars__item"
|
||||
:style="{ background: tint(entry.person.id) }"
|
||||
:title="entry.reason ? `${entry.person.displayName} — ${entry.reason}` : entry.person.displayName"
|
||||
type="button"
|
||||
@click="emit('tap', entry)"
|
||||
>
|
||||
<span>{{ initials(entry.person.displayName) }}</span>
|
||||
<span v-if="entry.origin === 'declared'" class="ld-avatars__glyph">
|
||||
<UIcon name="i-lucide-hand" />
|
||||
</span>
|
||||
<span v-else-if="entry.origin === 'computed'" class="ld-avatars__dot" />
|
||||
</button>
|
||||
<span v-if="overflow > 0" class="ld-avatars__more">+{{ overflow }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ld-avatars { display: inline-flex; align-items: center; }
|
||||
.ld-avatars__item {
|
||||
position: relative;
|
||||
width: var(--av-size);
|
||||
height: var(--av-size);
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(var(--av-size) * 0.36);
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
margin-left: -8px;
|
||||
box-shadow: 0 0 0 2px var(--mood-surface);
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
.ld-avatars__item:first-child { margin-left: 0; }
|
||||
.ld-avatars__item:hover { transform: translateY(-1px); z-index: 1; }
|
||||
.ld-avatars__glyph {
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
bottom: -3px;
|
||||
width: calc(var(--av-size) * 0.48);
|
||||
height: calc(var(--av-size) * 0.48);
|
||||
border-radius: 50%;
|
||||
background: var(--mood-surface);
|
||||
color: var(--mood-accent);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(var(--av-size) * 0.3);
|
||||
}
|
||||
.ld-avatars__dot {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
bottom: -1px;
|
||||
width: calc(var(--av-size) * 0.28);
|
||||
height: calc(var(--av-size) * 0.28);
|
||||
border-radius: 50%;
|
||||
background: var(--mood-accent);
|
||||
box-shadow: 0 0 0 2px var(--mood-surface);
|
||||
}
|
||||
.ld-avatars__more {
|
||||
margin-left: 0.4rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 700;
|
||||
color: var(--mood-text-muted);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user