Files
librodrome/app/composables/useTypewriter.ts
Yvv 082a17d09b Fix accueil : hero fade doux, icônes safelist, blocs cliquables, menu, dark fort
- Hero : réécriture composable timeout pur (plus de Transition callbacks)
  Animation fade opacity 1s très douce, lisible
- Icônes : safelist UnoCSS dans nuxt.config.ts (résout pastilles vides)
- Menu : mis à jour site.yml (Numérique/Économique/Citoyenne/Événement)
- Blocs : card entière cliquable, zone actions séparée (border-top)
- Économie du don : lié à /modele-eco (page chapitres préservée)
- Tarifs de l'eau : bouton SejeteralO (localhost:3009 / collectivites.librodrome.org)
- Dark theme fort : bg 220 12% 15%, surface 19%, surface-light 24%
- Config SejeteralO + Glibredecision dans app.config.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 04:08:47 +01:00

110 lines
2.4 KiB
TypeScript

export interface TypewriterSentence {
text: string
style?: 'title' | 'citation' | 'text'
stays?: boolean
separator?: boolean
}
interface SequenceOptions {
fadeMs?: number
holdMs?: number
gapMs?: number
}
export function useTypewriter(sentences: TypewriterSentence[], options: SequenceOptions = {}) {
const {
fadeMs = 1000,
holdMs = 2800,
gapMs = 300,
} = options
const currentText = ref('')
const currentStyle = ref<string>('title')
const isVisible = ref(false)
const lockedSentences = ref<TypewriterSentence[]>([])
const isComplete = ref(false)
let currentIdx = -1
let timer: ReturnType<typeof setTimeout> | null = null
function clearTimer() {
if (timer) {
clearTimeout(timer)
timer = null
}
}
function next() {
currentIdx++
if (currentIdx >= sentences.length) {
currentText.value = ''
isComplete.value = true
return
}
const sentence = sentences[currentIdx]
if (sentence.separator) {
lockedSentences.value = [...lockedSentences.value, { text: '', separator: true }]
}
// Set text while invisible
currentText.value = sentence.text
currentStyle.value = sentence.style || 'title'
// Fade in on next frame
requestAnimationFrame(() => {
isVisible.value = true
})
// After fade-in + hold → fade out
timer = setTimeout(() => {
isVisible.value = false
// After fade-out completes → lock if stays, then next
timer = setTimeout(() => {
if (sentence.stays) {
lockedSentences.value = [...lockedSentences.value, { ...sentence }]
}
timer = setTimeout(next, gapMs)
}, fadeMs)
}, fadeMs + holdMs)
}
function start() {
next()
}
function skipToEnd() {
clearTimer()
isVisible.value = false
currentText.value = ''
const locked: TypewriterSentence[] = []
for (const sentence of sentences) {
if (sentence.separator) {
locked.push({ text: '', separator: true })
}
if (sentence.stays) {
locked.push({ ...sentence })
}
}
lockedSentences.value = locked
currentIdx = sentences.length - 1
isComplete.value = true
}
onUnmounted(clearTimer)
return {
currentText: readonly(currentText),
currentStyle: readonly(currentStyle),
isVisible,
lockedSentences: readonly(lockedSentences),
isComplete: readonly(isComplete),
start,
skipToEnd,
}
}