From 2e51c3e7bdf711fecd5e0da52e188dd209a68e84 Mon Sep 17 00:00:00 2001 From: Rene Luria Date: Fri, 28 Aug 2026 16:07:01 +0200 Subject: [PATCH] docs: spec swipe-back vers l'accueil --- .../specs/2026-08-28-swipe-back-design.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-28-swipe-back-design.md diff --git a/docs/superpowers/specs/2026-08-28-swipe-back-design.md b/docs/superpowers/specs/2026-08-28-swipe-back-design.md new file mode 100644 index 0000000..383efc9 --- /dev/null +++ b/docs/superpowers/specs/2026-08-28-swipe-back-design.md @@ -0,0 +1,57 @@ +# 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)