feat: Enhance modal button behavior and validation feedback
- Updated modal button handling to disable all buttons by default, with exceptions for specific button types (e.g., cancel, close, delete). - Introduced a new validation mechanism that visually indicates empty required fields with red borders and error messages after a delay. - Improved the `useDialogAutoValidation` hook to manage button states based on field validation, ensuring a smoother user experience. - Added CSS animations to prevent flickering during validation state changes. Made-with: Cursor
This commit is contained in:
@@ -30,14 +30,20 @@
|
||||
│ │ │
|
||||
│ └─ 3단계: 버튼 비활성화 │
|
||||
│ │ │
|
||||
│ ├─ 대상: data-variant="default" 인 버튼 │
|
||||
│ │ (저장, 등록, 수정, 확인 등 — variant 미지정 = default) │
|
||||
│ ├─ 기본: 모달 내 모든 <button> 비활성화 │
|
||||
│ │ │
|
||||
│ ├─ 제외: outline, ghost, destructive, secondary │
|
||||
│ │ (취소, 닫기, X, 삭제 등) │
|
||||
│ ├─ 제외 (항상 활성): 아래 조건에 해당하는 버튼 │
|
||||
│ │ ├─ data-variant: outline, ghost, destructive, secondary│
|
||||
│ │ │ (취소, 닫기, 삭제 등 shadcn Button) │
|
||||
│ │ ├─ role: combobox, tab, switch, radio, checkbox │
|
||||
│ │ │ (폼 컨트롤, 탭 등 UI 요소) │
|
||||
│ │ ├─ data-slot="select-trigger" (Radix Select 트리거) │
|
||||
│ │ ├─ data-dialog-close (모달 닫기 X 버튼) │
|
||||
│ │ └─ data-action-type != save/submit │
|
||||
│ │ (ButtonPrimary 비저장 액션) │
|
||||
│ │ │
|
||||
│ ├─ 빈 필수 필드 있음 → 버튼 반투명 + 클릭 차단 │
|
||||
│ └─ 모든 필수 필드 입력됨 → 정상 활성화 │
|
||||
│ ├─ 빈 필수 필드 있음 → 나머지 버튼 반투명 + 클릭 차단 │
|
||||
│ └─ 모든 필수 필드 입력됨 → 전체 버튼 활성화 │
|
||||
│ │
|
||||
│ 제외 조건: │
|
||||
│ └─ 필수 필드가 0개인 모달 (자동 비활성) │
|
||||
@@ -79,27 +85,36 @@ if (!hasRequiredMark) return;
|
||||
|
||||
---
|
||||
|
||||
## 4. 버튼 식별: data-variant 속성 기반
|
||||
## 4. 버튼 비활성화: 기본 비활성 + 허용 목록 방식
|
||||
|
||||
### 원리
|
||||
|
||||
shadcn Button 컴포넌트의 `variant` 값을 `data-variant` 속성으로 DOM에 노출한다.
|
||||
텍스트 매칭 없이 버튼의 역할을 식별할 수 있다.
|
||||
모달 내 모든 `<button>` 요소를 기본 비활성화하고,
|
||||
안전한 버튼(취소, 닫기, 삭제, 폼 컨트롤 등)만 허용 목록으로 제외한다.
|
||||
저장 버튼의 구현 방식(shadcn Button / 네이티브 button)에 관계없이 동작한다.
|
||||
|
||||
### 비활성화 대상
|
||||
### 허용 목록 (항상 활성 유지)
|
||||
|
||||
| data-variant | 용도 | 훅 동작 |
|
||||
|:---:|------|:---:|
|
||||
| `default` | 저장, 등록, 수정, 확인 | 비활성화 대상 |
|
||||
| 셀렉터 | 용도 |
|
||||
|------|------|
|
||||
| `data-variant="outline"` | 취소 버튼 |
|
||||
| `data-variant="ghost"` | 닫기 버튼 |
|
||||
| `data-variant="destructive"` | 삭제 버튼 |
|
||||
| `data-variant="secondary"` | 보조 액션 |
|
||||
| `role="combobox"` | V2Select 드롭다운 트리거 |
|
||||
| `role="tab/switch/radio/checkbox"` | 폼 컨트롤, 탭 등 UI 요소 |
|
||||
| `data-slot="select-trigger"` | Radix Select 트리거 |
|
||||
| `data-dialog-close` | 모달 닫기 X 버튼 |
|
||||
| `data-action-type` != save/submit | ButtonPrimary 비저장 액션 |
|
||||
|
||||
### 제외 (건드리지 않음)
|
||||
### 비활성화 대상 (허용 목록에 해당하지 않는 모든 버튼)
|
||||
|
||||
| data-variant | 용도 |
|
||||
|:---:|------|
|
||||
| `outline` | 취소 |
|
||||
| `ghost` | 닫기, X 버튼 |
|
||||
| `destructive` | 삭제 |
|
||||
| `secondary` | 보조 액션 |
|
||||
| 예시 | 이유 |
|
||||
|------|------|
|
||||
| `<Button>저장</Button>` (data-variant="default") | shadcn 기본 버튼 |
|
||||
| `<button data-action-type="save">` | ButtonPrimary 저장 액션 |
|
||||
| `<button data-action-type="submit">` | ButtonPrimary 제출 액션 |
|
||||
| 기타 variant/role 없는 `<button>` | 알 수 없는 버튼은 안전하게 차단 |
|
||||
|
||||
---
|
||||
|
||||
@@ -128,14 +143,14 @@ useDialogAutoValidation 실행
|
||||
초기 검증 실행 (50ms 후)
|
||||
│
|
||||
├─ 빈 필수 필드 발견
|
||||
│ ├─ 해당 input에 border-destructive 클래스 추가
|
||||
│ ├─ 해당 input에 data-validation-empty 속성 추가 (CSS 지연 테두리)
|
||||
│ ├─ input 아래에 "필수 입력 항목입니다" 에러 메시지 주입
|
||||
│ └─ data-variant="default" 버튼 비활성화
|
||||
│ └─ 허용 목록 외 모든 버튼 비활성화
|
||||
│
|
||||
├─ 모든 필수 필드 입력됨
|
||||
│ ├─ 에러 메시지 제거
|
||||
│ ├─ border-destructive 제거
|
||||
│ └─ 버튼 활성화
|
||||
│ ├─ data-validation-empty 속성 제거
|
||||
│ └─ 전체 버튼 활성화
|
||||
│
|
||||
▼
|
||||
이벤트 리스너 등록
|
||||
@@ -150,9 +165,10 @@ useDialogAutoValidation 실행
|
||||
클린업
|
||||
├─ 이벤트 리스너 제거
|
||||
├─ MutationObserver 해제
|
||||
├─ 폴링 타이머 해제
|
||||
├─ 주입된 에러 메시지 제거
|
||||
├─ 버튼 비활성화 상태 복원
|
||||
└─ border-destructive 클래스 제거
|
||||
└─ data-validation-empty 속성 제거
|
||||
```
|
||||
|
||||
---
|
||||
@@ -163,7 +179,9 @@ useDialogAutoValidation 실행
|
||||
|------|------|
|
||||
| `frontend/lib/hooks/useDialogAutoValidation.ts` | 자동 검증 훅 본체 |
|
||||
| `frontend/components/ui/button.tsx` | data-variant 속성 노출 |
|
||||
| `frontend/components/ui/dialog.tsx` | DialogContent에서 훅 호출 |
|
||||
| `frontend/components/ui/dialog.tsx` | DialogContent에서 훅 호출, data-dialog-close 속성 |
|
||||
| `frontend/lib/registry/components/v2-button-primary/ButtonPrimaryComponent.tsx` | data-action-type 속성 노출 |
|
||||
| `frontend/app/globals.css` | CSS 애니메이션 지연 (깜빡임 방지) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user