fix(exit-hold): bar 진행 표시 즉시 반영 및 리셋 애니메이션 수정

맥락:

- 몰입 OFF의 나가기 롱프레스 bar에서 진행 표시가 즉시 보이지 않고 완료 시 0으로 역방향 축소되는 문제를 해결하기 위해

변경사항:

- bar 진행 표시를 JS width 갱신에서 CSS keyframes 기반으로 교체

- 키프레임에 가속 규칙 반영(0.05초 20%, 1.0초 100%)

- 완료 후 100% 상태를 짧게 유지한 뒤 언마운트하도록 훅 상태(isCompleted) 보강

- progress fill의 rounded 캡을 제거해 끝단 직선화

- docs/90_current_state.md, docs/session_brief.md 최신 상태 반영

검증:

- npx tsc --noEmit

세션-상태: bar 롱프레스 진행은 눌렀을 때 즉시 보이고 완료 리셋 시 역방향 축소가 사라짐

세션-다음: 롱프레스 인지성 보조 카피 도입 여부 검토

세션-리스크: bar(CSS)와 ring(JS) 진행 로직이 분리되어 향후 규칙 변경 시 동시 점검 필요
This commit is contained in:
2026-02-27 14:36:31 +09:00
parent 73e7d5004c
commit 20638b69a4
5 changed files with 92 additions and 14 deletions

View File

@@ -18,7 +18,7 @@ export const ExitHoldButton = ({
onConfirm,
className,
}: ExitHoldButtonProps) => {
const { progress, isHolding, start, cancel } = useHoldToConfirm(onConfirm);
const { progress, isHolding, isCompleted, start, cancel } = useHoldToConfirm(onConfirm);
const ringOffset = RING_CIRCUMFERENCE * (1 - progress);
const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
@@ -101,15 +101,20 @@ export const ExitHoldButton = ({
onKeyUp={handleKeyUp}
onClick={(event) => event.preventDefault()}
className={cn(
'group relative overflow-hidden rounded-lg bg-white/8 px-2.5 py-1.5 text-xs text-white/82 transition hover:bg-white/14 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-200/80',
'relative overflow-hidden rounded-lg bg-white/8 px-2.5 py-1.5 text-xs text-white/82 transition hover:bg-white/14 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-200/80',
className,
)}
>
<span
aria-hidden
className="absolute inset-y-0 left-0 rounded-lg bg-sky-200/24 transition-[width] duration-75"
style={{ width: `${progress * 100}%` }}
/>
{isHolding || isCompleted ? (
<span
aria-hidden
className={cn(
'absolute inset-0 z-0 origin-left transform-gpu bg-sky-200/24 rounded-none',
isHolding && 'animate-[exit-hold-bar-fill_1000ms_linear_forwards]',
)}
style={isCompleted ? { transform: 'scaleX(1)' } : undefined}
/>
) : null}
<span className="relative z-10 inline-flex items-center gap-1">
<span aria-hidden></span>
<span></span>