133 lines
4.6 KiB
Dart
133 lines
4.6 KiB
Dart
import 'dart:math' as math;
|
||
|
||
import 'package:flutter/material.dart';
|
||
|
||
import '../../../core/theme/tokens.dart';
|
||
import '../../../core/ui/app_icons.dart';
|
||
import '../../quest/widgets/quest_card_tile.dart'
|
||
show QuestIconBox, questImageFor, questSubLine;
|
||
import '../home_data.dart';
|
||
|
||
/// 홈 「TODAY'S QUEST」 묶음 — Figma 「홈」 `2097:16428`: 343×270 · r12 안에 343×90 줄
|
||
/// 세 개를 간격 없이 쌓는다. 앞 두 줄에 `#242424` 1 획(사본 spec) — 줄 사이 구분선으로 보인다.
|
||
///
|
||
/// 항목은 설명 프레임 `2097:16397` 「관리자 퀘스트 1·2·3 — 최신순 최대 3개 · 클릭 시 해당
|
||
/// 퀘스트 상세」. 사용자 생성 퀘스트는 저장소가 거른다.
|
||
class QuestRows extends StatelessWidget {
|
||
const QuestRows({super.key, required this.quests, required this.onTap});
|
||
|
||
final List<QuestSummary> quests;
|
||
final ValueChanged<QuestSummary> onTap;
|
||
|
||
/// 시안 줄 수. 나머지는 「더 보기」(퀘스트 목록)에서 본다.
|
||
static const maxRows = 3;
|
||
|
||
@override
|
||
Widget build(BuildContext context) => ClipRRect(
|
||
borderRadius: BorderRadius.circular(12),
|
||
child: Column(
|
||
children: [
|
||
for (final (i, q) in quests.take(maxRows).indexed)
|
||
QuestRow(
|
||
quest: q,
|
||
divider: i < math.min(quests.length, maxRows) - 1,
|
||
onTap: () => onTap(q),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 한 줄 — `2097:16428` 「출석체크 / 오늘의 출석체크」 행: #1A1A1C · 패딩 20 · 왼쪽 50×50
|
||
/// #2E2E2E r16 아이콘 상자(42 이미지) · 간격 19 · 제목 18/600 + 보조 14/500(간격 4) ·
|
||
/// 오른쪽 chevron 24 #4F4F4F.
|
||
///
|
||
/// 보조 줄은 카테고리(사본 「오늘의 출석체크」 · 「커뮤니티」)다. 「홈」 프레임에는 보상 숫자 ·
|
||
/// 진행 상태가 없어 그리지 않는다. 타임어택 남은 시간도 그리지 않는다 — 설명 프레임
|
||
/// `2097:16397` 데이터는 「어드민에서 등록된 퀘스트」뿐이고, FR-QST-06 · 원장 C-94 의 카운트다운
|
||
/// 표시 위치는 퀘스트 목록 · 상세다.
|
||
class QuestRow extends StatelessWidget {
|
||
const QuestRow({
|
||
super.key,
|
||
required this.quest,
|
||
this.onTap,
|
||
this.divider = false,
|
||
});
|
||
|
||
final QuestSummary quest;
|
||
final VoidCallback? onTap;
|
||
|
||
/// 아래 변 `#242424` 1 획.
|
||
final bool divider;
|
||
|
||
static const height = 90.0;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Material(
|
||
color: AppColors.cosmicSurfaceContainer,
|
||
shape: divider
|
||
? const Border(bottom: BorderSide(color: Color(0xFF242424)))
|
||
: null,
|
||
child: InkWell(
|
||
onTap: onTap,
|
||
child: ConstrainedBox(
|
||
// 글자 확대 때는 늘어난다 — 고정하면 잘린다.
|
||
constraints: const BoxConstraints(minHeight: height),
|
||
child: Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||
child: Row(
|
||
children: [
|
||
QuestIconBox(image: questImageFor(quest.type, quest.title)),
|
||
const SizedBox(width: 19),
|
||
Expanded(
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
quest.title,
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
style: const TextStyle(
|
||
fontFamily: AppFonts.body,
|
||
fontSize: 18,
|
||
fontWeight: FontWeight.w600,
|
||
height: 20 / 18,
|
||
color: AppColors.onCosmic,
|
||
),
|
||
),
|
||
const SizedBox(height: 4),
|
||
// 진행중 탭 카드와 같은 일시 정지 표기(원장 C-83).
|
||
_sub(questSubLine(quest.category ?? '', quest.pausedSession)),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(width: AppSpace.s2),
|
||
const ImageIcon(
|
||
AppIcons.chevronRight,
|
||
size: 24,
|
||
color: Color(0xFF4F4F4F),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
Widget _sub(String text) => Text(
|
||
text,
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
style: const TextStyle(
|
||
fontFamily: AppFonts.body,
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w500,
|
||
height: 20 / 14,
|
||
color: AppColors.onCosmicVariant,
|
||
),
|
||
);
|