fix(space): intent 카드 dismissal 규칙 정리
This commit is contained in:
@@ -105,6 +105,7 @@ Intent Card는 아래 2개 상태만 가진다.
|
||||
- pointer leave
|
||||
- focus out
|
||||
- expand affordance 재클릭
|
||||
- expanded 상태에서 card 바깥 pointer down
|
||||
- recovery tray가 열릴 때
|
||||
|
||||
중요:
|
||||
@@ -112,6 +113,19 @@ Intent Card는 아래 2개 상태만 가진다.
|
||||
- `pause / return / next-beat / complete / refocus` tray가 열려 있는 동안에는 목표 카드는 강제로 collapsed 상태를 유지한다
|
||||
- tray가 이미 의사결정 레이어이기 때문에, base card까지 expanded 상태면 배경을 이중으로 가리게 된다
|
||||
|
||||
### dismissal rule
|
||||
|
||||
- expanded goal card는 `outside click / outside tap`으로 접혀도 된다
|
||||
- 이 동작은 base card의 ephemeral expansion에만 적용한다
|
||||
- `refocus / next-beat / return / complete`처럼 의사결정을 요구하는 tray는 outside click으로 닫히면 안 된다
|
||||
- decision tray는 반드시 명시적 액션으로만 닫는다
|
||||
- `취소`
|
||||
- `적용`
|
||||
- `여기까지 끝내기`
|
||||
- `잠깐 쉬기`
|
||||
- `다음 블록 이어가기`
|
||||
- 즉, `/space`에서 가볍게 접히는 것은 `expanded rail`뿐이고, 실질적인 state change layer는 dismissible popover로 취급하지 않는다
|
||||
|
||||
---
|
||||
|
||||
## 5. 시각 구조
|
||||
|
||||
@@ -60,6 +60,7 @@ Last Updated: 2026-03-14
|
||||
- 상시 큰 goal 카드 대신 idle에서는 goal 1줄과 작은 expand affordance만 남는 collapsed glass rail 구조로 변경
|
||||
- hover / focus / toggle에서만 expanded card로 열리며, 이때만 microStep과 `이번 목표 완료` 액션이 노출됨
|
||||
- recovery tray(`pause / return / next-beat / complete / refocus`)가 열릴 때는 base card가 강제로 collapsed 상태를 유지하도록 정리
|
||||
- expanded rail은 outside click으로 접히지만, recovery tray는 outside click으로 닫히지 않고 명시적 액션으로만 닫힘
|
||||
|
||||
- Focus Entry Surface / Execution Surface 재정의:
|
||||
- `/app`을 planning home이 아니라 hero-first focus entry surface로 재구성
|
||||
|
||||
@@ -64,6 +64,7 @@ Last Updated: 2026-03-14
|
||||
- idle에서는 goal 1줄만 남는 얇은 glass rail로 줄였다.
|
||||
- microStep과 `이번 목표 완료`는 expanded 상태에서만 드러난다.
|
||||
- recovery tray가 열리면 base card는 자동으로 collapsed 상태를 유지한다.
|
||||
- expanded rail은 outside click으로 접히지만, decision tray는 outside click으로 닫히지 않는다.
|
||||
- `/app`을 single-goal commitment gate로 다시 줄였다.
|
||||
- 2-step ritual setup을 제거했다.
|
||||
- current session이 있으면 `Resume` UI만 보여주고, `/space`로 이어가기만 제안한다.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { copy } from '@/shared/i18n';
|
||||
import { cn } from '@/shared/lib/cn';
|
||||
|
||||
@@ -28,6 +28,7 @@ export const IntentCapsule = ({
|
||||
const [isPinnedExpanded, setPinnedExpanded] = useState(false);
|
||||
const [isHovered, setHovered] = useState(false);
|
||||
const [isFocusWithin, setFocusWithin] = useState(false);
|
||||
const sectionRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
const normalizedMicroStep = microStep?.trim() ? microStep.trim() : null;
|
||||
const isExpanded = showActions && (isPinnedExpanded || isHovered || isFocusWithin);
|
||||
@@ -35,6 +36,34 @@ export const IntentCapsule = ({
|
||||
const microGlyphClass =
|
||||
'inline-flex h-5 w-5 items-center justify-center rounded-full border border-white/18 text-white/62 transition-all duration-200 hover:border-white/32 hover:text-white focus-visible:border-white/32 focus-visible:text-white disabled:cursor-default disabled:opacity-30';
|
||||
|
||||
useEffect(() => {
|
||||
if (!isExpanded || !showActions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlePointerDown = (event: PointerEvent) => {
|
||||
const target = event.target;
|
||||
|
||||
if (!(target instanceof Node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sectionRef.current?.contains(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPinnedExpanded(false);
|
||||
setHovered(false);
|
||||
setFocusWithin(false);
|
||||
};
|
||||
|
||||
document.addEventListener('pointerdown', handlePointerDown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('pointerdown', handlePointerDown);
|
||||
};
|
||||
}, [isExpanded, showActions]);
|
||||
|
||||
const handleGoalClick = () => {
|
||||
if (!canInteract) {
|
||||
return;
|
||||
@@ -59,6 +88,7 @@ export const IntentCapsule = ({
|
||||
return (
|
||||
<div className="pointer-events-none flex w-full">
|
||||
<section
|
||||
ref={sectionRef}
|
||||
className={cn(
|
||||
'pointer-events-auto relative overflow-hidden border text-white transition-[width,padding,background-color,border-color,box-shadow] duration-200 ease-out',
|
||||
isExpanded
|
||||
|
||||
Reference in New Issue
Block a user