mirror of
https://github.com/IliesAllali/parenthese.git
synced 2026-11-06 23:32:08 +00:00
Merge pull request #170 from IliesAllali/feat/mouvement-fiche
Fiche : apparaît d'un coup sur ordinateur, feuille plus vive, résistance vers le haut, coins continus
This commit is contained in:
5 files changed
+62
-17
No files matched your search
@@ -21,15 +21,10 @@
|
||||
box-shadow: var(--pz-shadow-lg);
|
||||
font-family: var(--pz-sans);
|
||||
color: var(--pz-ink);
|
||||
animation: pc-in 260ms var(--pz-ease) both;
|
||||
/* Pas d'entrée animée sur ordinateur : on ouvre une fiche des dizaines de fois par visite, elle apparaît d'un coup (Patch, 27/10) */
|
||||
}
|
||||
.person-card--edit-mode { border-color: var(--pz-a300); }
|
||||
|
||||
@keyframes pc-in {
|
||||
from { opacity: 0; transform: translateX(-12px) scale(.985); filter: blur(3px); }
|
||||
to { opacity: 1; transform: none; filter: none; }
|
||||
}
|
||||
|
||||
.pc-grab { display: none; }
|
||||
.pc-close { position: absolute; top: 12px; right: 12px; }
|
||||
.pc-hidden-input { display: none; }
|
||||
@@ -250,8 +245,10 @@
|
||||
left: 0; right: 0; bottom: 0; width: auto;
|
||||
max-height: 78dvh; padding: 10px 18px calc(18px + env(safe-area-inset-bottom));
|
||||
border-radius: 26px 26px 0 0; border-bottom: 0;
|
||||
/* Ombre pleine sous la feuille, en premier pour passer au-dessus de l'ombre floue : tirée vers le haut (40 px au plus, sheetOffset), elle ne laisse pas voir l'arbre dessous */
|
||||
box-shadow: 0 40px 0 var(--pz-surface), var(--pz-shadow-lg);
|
||||
/* Pas de fill-mode « both » : une fois l'entrée finie, le glisser (style inline) reprend la main sur transform */
|
||||
animation: pc-sheet 400ms var(--pz-ease-drawer) backwards;
|
||||
animation: pc-sheet 240ms var(--pz-ease-drawer) backwards;
|
||||
transition: transform 240ms var(--pz-ease-drawer);
|
||||
}
|
||||
/* Pendant le glisser, la feuille suit le doigt sans transition */
|
||||
@@ -265,11 +262,22 @@
|
||||
.pc-name { font-size: 24px; }
|
||||
@keyframes pc-sheet { from { transform: translateY(100%); } to { transform: none; } }
|
||||
}
|
||||
/* Coins continus là où le navigateur sait les dessiner (Chromium) ; ailleurs, les arcs de cercle d'avant. Le rayon grandit
|
||||
parce qu'un squircle de même rayon paraît plus anguleux. */
|
||||
@supports (corner-shape: squircle) {
|
||||
.person-card { corner-shape: squircle; border-radius: 34px; }
|
||||
.pc-photo { corner-shape: squircle; border-radius: 38%; }
|
||||
.pc-tile, .pc-list-item { corner-shape: squircle; border-radius: 18px; }
|
||||
@media (max-width: 768px) { .person-card { border-radius: 40px 40px 0 0; } }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.person-card { animation: pz-fade 200ms ease both; }
|
||||
.person-card { transition-duration: 1ms; }
|
||||
}
|
||||
/* Mouvements réduits : la feuille du téléphone ne monte plus, elle s'affiche en fondu court */
|
||||
@media (prefers-reduced-motion: reduce) and (max-width: 768px) {
|
||||
.person-card { animation: pz-fade 200ms ease both; }
|
||||
}
|
||||
|
||||
/* Prénom inconnu (issue Anaïs, 18/10) : libellé en italique, case à cocher sous les champs de nom */
|
||||
.pc-name-unknown { font-style: italic; font-weight: 300; }
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
padding: 0 14px calc(16px + env(safe-area-inset-bottom, 0px));
|
||||
background: var(--pz-surface);
|
||||
border-radius: 24px 24px 0 0;
|
||||
box-shadow: 0 -12px 40px rgba(42, 38, 34, 0.16);
|
||||
/* Ombre pleine sous la feuille, en premier pour passer au-dessus de l'ombre floue : tirée vers le haut (40 px au plus, sheetOffset), elle ne laisse pas voir la page dessous */
|
||||
box-shadow: 0 40px 0 var(--pz-surface), 0 -12px 40px rgba(42, 38, 34, 0.16);
|
||||
/* Pas de fill-mode : une fois l'entrée finie, le glisser (style inline) reprend la main */
|
||||
animation: navmSheetIn 320ms var(--pz-ease-drawer);
|
||||
transition: transform 240ms var(--pz-ease-drawer);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
canStartSheetDrag,
|
||||
shouldCloseSheet,
|
||||
sheetDragIntent,
|
||||
sheetOffset,
|
||||
velocityBetween,
|
||||
} from '../utils/sheetDrag'
|
||||
|
||||
@@ -35,7 +36,7 @@ export function useSheetMode() {
|
||||
/**
|
||||
* Glisser pour fermer une feuille du bas.
|
||||
* - Le geste ne part que des éléments de `zone` (sélecteur CSS) et jamais d'un champ de saisie ni d'une sélection de texte.
|
||||
* - La feuille suit le doigt (transform inline) et ne monte jamais au-delà de sa position de repos.
|
||||
* - La feuille suit le doigt (transform inline) ; au-dessus de sa position de repos elle résiste (`sheetOffset`) et revient au lâcher.
|
||||
* - Au lâcher : `closes` décide (par défaut, un tiers de la hauteur ou un lâcher rapide). Fermer attend la fin de la sortie,
|
||||
* puis appelle `onClose`. Si `onClose` renvoie `false` (enregistrement refusé), la feuille revient.
|
||||
* - Après un vrai glisser, le clic qui suit est avalé (pas de bouton déclenché au lâcher).
|
||||
@@ -107,7 +108,7 @@ export function useSheetDrag({
|
||||
}
|
||||
g.prev = g.last
|
||||
g.last = { y: event.clientY, t: event.timeStamp }
|
||||
setOffset(Math.max(0, dy))
|
||||
setOffset(sheetOffset(dy))
|
||||
}
|
||||
|
||||
const onPointerUp = (event) => {
|
||||
@@ -142,6 +143,6 @@ export function useSheetDrag({
|
||||
return {
|
||||
dragging,
|
||||
bind: { onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onClickCapture },
|
||||
style: offset > 0 ? { transform: `translateY(${offset}px)` } : undefined,
|
||||
style: offset !== 0 ? { transform: `translateY(${offset}px)` } : undefined,
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,21 @@ export function canStartSheetDrag(target, zoneSelector, selectionText = '') {
|
||||
return !target.closest(NO_DRAG_SELECTOR)
|
||||
}
|
||||
|
||||
/** Intention du mouvement depuis l'appui : 'pending' (encore trop petit), 'down' (glisser vers le bas), 'other' (haut ou horizontal) */
|
||||
/** Intention du mouvement depuis l'appui : 'pending' (encore trop petit), 'down' ou 'up' (glisser vertical), 'other' (horizontal) */
|
||||
export function sheetDragIntent(dx, dy) {
|
||||
if (Math.abs(dx) < DIRECTION_LOCK_PX && Math.abs(dy) < DIRECTION_LOCK_PX) return 'pending'
|
||||
if (dy > 0 && dy >= Math.abs(dx)) return 'down'
|
||||
return 'other'
|
||||
if (Math.abs(dy) < Math.abs(dx)) return 'other'
|
||||
return dy > 0 ? 'down' : 'up'
|
||||
}
|
||||
|
||||
// Au-dessus de sa position de repos, la feuille cède de moins en moins, sans jamais dépasser ce plafond (px)
|
||||
export const RUBBER_MAX_PX = 40
|
||||
const RUBBER_SOFTNESS_PX = 120
|
||||
|
||||
/** Décalage de la feuille pour un déplacement du doigt : suit vers le bas, résiste vers le haut (négatif, borné) */
|
||||
export function sheetOffset(dy) {
|
||||
if (dy >= 0) return dy
|
||||
return -RUBBER_MAX_PX * (1 - 1 / (1 + -dy / RUBBER_SOFTNESS_PX))
|
||||
}
|
||||
|
||||
/** Vitesse (px/ms, positive vers le bas) entre deux points {y, t}. Zéro si les points sont trop éloignés dans le temps. */
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
NO_DRAG_SELECTOR,
|
||||
RUBBER_MAX_PX,
|
||||
canStartSheetDrag,
|
||||
shouldCloseSheet,
|
||||
sheetDragIntent,
|
||||
sheetOffset,
|
||||
velocityBetween,
|
||||
} from './sheetDrag'
|
||||
|
||||
@@ -55,9 +57,32 @@ describe('sheetDragIntent (direction)', () => {
|
||||
expect(sheetDragIntent(10, 20)).toBe('down')
|
||||
})
|
||||
|
||||
it('rejette le haut et l\'horizontal', () => {
|
||||
expect(sheetDragIntent(0, -20)).toBe('other')
|
||||
it('reconnaît un glisser vers le haut', () => {
|
||||
expect(sheetDragIntent(0, -20)).toBe('up')
|
||||
expect(sheetDragIntent(-10, -20)).toBe('up')
|
||||
})
|
||||
|
||||
it('rejette l\'horizontal', () => {
|
||||
expect(sheetDragIntent(30, 20)).toBe('other')
|
||||
expect(sheetDragIntent(-30, -20)).toBe('other')
|
||||
})
|
||||
})
|
||||
|
||||
describe('sheetOffset (résistance au-dessus du repos)', () => {
|
||||
it('suit le doigt vers le bas', () => {
|
||||
expect(sheetOffset(0)).toBe(0)
|
||||
expect(sheetOffset(120)).toBe(120)
|
||||
})
|
||||
|
||||
it('cède de moins en moins vers le haut, sans dépasser le plafond', () => {
|
||||
const a = sheetOffset(-60)
|
||||
const b = sheetOffset(-120)
|
||||
const c = sheetOffset(-2000)
|
||||
expect(a).toBeLessThan(0)
|
||||
expect(b).toBeLessThan(a)
|
||||
expect(-b).toBeLessThan(120)
|
||||
expect(b - c).toBeLessThan(0 - b)
|
||||
expect(c).toBeGreaterThan(-RUBBER_MAX_PX)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user