106 lines
4.1 KiB
Markdown
106 lines
4.1 KiB
Markdown
# 입력 위젯 가이드
|
|
|
|
Superport v2의 폼 UI는 shadcn_ui 구성 요소를 기반으로 하며, 아래 지침을 따를 때 레이아웃과 상호작용이 일관되게 유지된다.
|
|
|
|
## 1. 기본 컨테이너 — `SuperportFormField`
|
|
- 라벨/필수 표시/보조 설명/에러 메시지를 하나의 빌딩 블록으로 묶는다.
|
|
- 라벨은 좌측 정렬, 12pt, 필수 항목은 `*`(파괴색)으로 표시한다.
|
|
- 자식 위젯은 `ShadInput`, `ShadSelect`, `SuperportDatePickerButton` 등 어떤 입력 요소든 가능하다.
|
|
- 에러 문구는 상단 Validator에서 내려오는 한글 메시지를 그대로 사용한다.
|
|
|
|
```dart
|
|
SuperportFormField(
|
|
label: '창고',
|
|
required: true,
|
|
caption: '입고가 진행될 창고를 선택하세요.',
|
|
errorText: state.errorMessage,
|
|
child: ShadSelect<String>(
|
|
initialValue: controller.selectedWarehouse,
|
|
options: warehouses.map((w) => ShadOption(value: w.id, child: Text(w.name))).toList(),
|
|
onChanged: controller.onWarehouseChanged,
|
|
),
|
|
);
|
|
```
|
|
|
|
## 2. 텍스트 입력 — `SuperportTextInput`
|
|
- `ShadInput`에 공통 프리셋을 적용한 래퍼.
|
|
- 플레이스홀더는 한글 문장형으로 작성하고, 검색 필드라면 돋보기 아이콘을 `leading`으로 배치한다.
|
|
- 여러 줄 입력은 `maxLines` 변경만으로 처리한다.
|
|
|
|
```dart
|
|
SuperportFormField(
|
|
label: '비고',
|
|
child: SuperportTextInput(
|
|
controller: remarkController,
|
|
placeholder: const Text('추가 설명을 입력하세요.'),
|
|
maxLines: 3,
|
|
),
|
|
);
|
|
```
|
|
|
|
## 3. 선택 컴포넌트 — `ShadSelect`
|
|
- 단일 선택은 `ShadSelect<T>`를 그대로 사용하고, `SuperportFormField`로 라벨만 감싼다.
|
|
- 다중 선택이 필요한 경우 `ShadSelect.multiple` 과 토큰(UiChip) 스타일을 조합한다.
|
|
- 최초 옵션은 `전체`/`선택하세요`처럼 명확한 기본값을 제공한다.
|
|
|
|
```dart
|
|
SuperportFormField(
|
|
label: '상태',
|
|
required: true,
|
|
child: ShadSelect<OrderStatus?>(
|
|
initialValue: controller.pendingStatus,
|
|
selectedOptionBuilder: (_, value) => Text(value?.label ?? '전체 상태'),
|
|
options: [
|
|
const ShadOption(value: null, child: Text('전체 상태')),
|
|
for (final status in OrderStatus.values)
|
|
ShadOption(value: status, child: Text(status.label)),
|
|
],
|
|
onChanged: controller.onStatusChanged,
|
|
),
|
|
);
|
|
```
|
|
|
|
## 4. 토글 — `SuperportSwitchField`
|
|
- 스위치 단독 사용 시 라벨·캡션 레이아웃을 제공한다.
|
|
- 접근성 관점에서 토글 설명은 문장형으로 작성한다.
|
|
|
|
```dart
|
|
SuperportSwitchField(
|
|
label: '파트너사 전용',
|
|
value: controller.isPartnerOnly,
|
|
onChanged: controller.onPartnerOnlyChanged,
|
|
caption: '활성화 시 파트너사만 접근할 수 있습니다.',
|
|
);
|
|
```
|
|
|
|
## 5. 날짜/기간 — `SuperportDatePickerButton`
|
|
- 단일 날짜는 `SuperportDatePickerButton`, 기간은 `SuperportDateRangePickerButton`을 사용한다.
|
|
- 포맷은 기본적으로 `yyyy-MM-dd`, 필요 시 `dateFormat`으로 주입.
|
|
- 기간 선택은 `firstDate`/`lastDate` 범위를 명시해 엣지 케이스를 제한한다.
|
|
|
|
```dart
|
|
SuperportFormField(
|
|
label: '처리 기간',
|
|
child: SuperportDateRangePickerButton(
|
|
value: controller.pendingRange,
|
|
onChanged: controller.onRangeChanged,
|
|
firstDate: DateTime(2020),
|
|
lastDate: DateTime(2030),
|
|
),
|
|
);
|
|
```
|
|
|
|
## 6. 검증 메시지
|
|
- Validator는 필수 오류 → 형식 오류 → 업무 규칙 순서로 확인하고, 메시지는 `SuperportFormField.errorText`로 전달한다.
|
|
- 포커스 이동 시 즉시 에러를 표시하며, 성공 시 `caption`으로 가이드를 남겨 재입력을 돕는다.
|
|
|
|
## 7. 레이아웃
|
|
- 가로 240px/500px 프리셋은 `SizedBox`로 감싸 사용하며, 반응형 환경에서는 `ResponsiveLayoutSlot`(섹션 13 참조)을 이용한다.
|
|
- 두 줄 이상 배치 시 `Wrap` + `spacing:16`/`runSpacing:16`을 기본으로 한다.
|
|
|
|
## 8. 샘플 코드 경로
|
|
- `lib/widgets/components/form_field.dart`
|
|
- `lib/features/inventory/inbound/presentation/pages/inbound_page.dart` — 입고 등록 모달
|
|
|
|
위 가이드를 준수하면 폼 간 스타일과 상호작용 규칙을 동일하게 유지할 수 있다.
|