Files
livrets/docs/superpowers/specs/2026-08-28-swipe-back-design.md
T

58 lines
3.2 KiB
Markdown

# Design : swipe-back vers l'accueil — Livrets
Date : 2026-08-28
Statut : Validé par le produit
## 1. Objectif
Permettre de revenir au menu principal par un geste de swipe (glissement depuis le bord gauche vers la droite, style iOS) depuis l'écran des scores (Results) et l'historique (History), en complément des boutons existants. Fonctionne au doigt (Android/iOS) et à la souris (desktop).
## 2. Décisions clés
| Décision | Choix |
|---|---|
| Direction | Horizontal, bord gauche → droite (convention iOS back) |
| Écrans actifs | Results + History (pas Game : drag de jeu, pas de back simple) |
| Feedback | Preview interactive : l'écran suit le doigt, Home révélé derrière |
| Validation | offset > 40 % de la largeur OU vélocité > seuil, sinon retour en place animé |
| Zone de démarrage | 24 dp du bord gauche uniquement (préserve le scroll vertical des listes) |
| Implémentation | Wrapper `SwipeBack` réutilisable + `Animatable` + `pointerInput` custom |
| Desktop | Drag souris : même chemin de code (`pointerInput` capte la souris) |
Rejeté : backstack général + transitions animées (refactor disproportionné pour une profondeur de navigation = 1) ; API `anchoredDraggable` (pas de détection bord-native, code custom nécessaire autour) ; swipe vertical descendant.
## 3. Architecture
- `sharedUI/src/commonMain/kotlin/com/livrets/ui/SwipeBack.kt` (nouveau) :
```kotlin
@Composable
fun SwipeBack(
onBack: () -> Unit,
background: @Composable () -> Unit,
content: @Composable () -> Unit
)
```
- Structure : `Box { background(); Box(translationX = offset) { content() } }`
- Fond composé uniquement quand `offset > 0` (aucun coût hors drag)
- Détection : `pointerInput` + `awaitEachGesture` — toucher initial dans les 24 dp du bord gauche, premier dépassement de slop horizontal (drag vertical abandonné, scroll des listes intact)
- Suivi : offset `Animatable(0f)` clampé ≥ 0, appliqué via `graphicsLayer.translationX` (pas de recomposition de layout)
- Relâchement : back si offset > 40 % largeur ou vélocité ≥ 1000 dp/s (seuil fling, converti en px/s via densité) ; sinon `animateTo(0)` ; à la validation `animateTo(largeur)` puis `onBack()`
- Constantes de geste (24 dp, 40 %, vélocité) centralisées dans un objet dédié pour testabilité
- `App.kt` : Results et History wrappés par `SwipeBack(onBack = { screen = Screen.Home }, background = { HomeScreen(...) })`
## 4. Faisabilité
`pointerInput`/`Animatable`/`graphicsLayer` sont dans Compose Multiplatform 1.12 (déjà en dépendance), disponibles sur les 3 cibles ; la souris est un pointer standard côté desktop.
## 5. Tests & vérifications
- Test unitaire JVM : logique de décision de relâchement (offset/vélocité → back ou settle) dans `commonTest`
- Vérification manuelle : drag souris sur desktop ; swipe sur device/simulateur si disponible
- Régression : `:sharedUI:jvmTest` + `./gradlew build` (les 3 cibles)
## 6. Hors périmètre
- `BackHandler` Android (le bouton système ferme l'activité — comportement préexistant, suite possible)
- Animation parallaxe de l'écran Home pendant le drag
- Geste de retour sur Game ou en profondeur (backstack)