feat(widget): fill quest list adaptively in goal progress widget
목표 진행률 위젯의 다짐 목록이 고정 3개(중형)/8개(대형) 제한이었던 것을 QuestBarsColumn으로 교체 — 셀의 실제 높이에 들어가는 만큼 최대한 표시 (행 14pt+간격 6pt 기준 계산)하고, 넘치는 개수는 헤더 오른쪽에 '+N' 표기. 기기·패밀리가 커지면 자동으로 더 보인다. 그 외 개수 제한 검토: 다짐 현황 위젯 그리드(4/8/16)는 스펙 고정, 통계 차트 시리즈(4/6)는 가독성 제한, 잠금화면 점(6)은 공간 제한이라 유지. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
5916e17b25
commit
c6959950c2
@ -181,35 +181,63 @@ struct QuestBarRow: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// 다짐 하루 진행률 목록 카드 — 고정 개수 대신 주어진 높이에 들어가는 만큼 최대한 채운다.
|
||||
/// 다 못 담으면 헤더 오른쪽에 "+N" 표기. (중형/대형 구성들이 공용)
|
||||
struct QuestBarsColumn: View {
|
||||
let quests: [QuestCellSnapshot]
|
||||
|
||||
private let rowHeight: CGFloat = 14
|
||||
private let rowSpacing: CGFloat = 6
|
||||
private let headerHeight: CGFloat = 17
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
let fitCount = max(Int((geo.size.height - headerHeight + rowSpacing) / (rowHeight + rowSpacing)), 1)
|
||||
let shown = Array(quests.prefix(fitCount))
|
||||
VStack(alignment: .leading, spacing: rowSpacing) {
|
||||
HStack {
|
||||
Text("다짐 (하루)")
|
||||
.font(.system(size: 9, weight: .semibold))
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
if quests.count > shown.count {
|
||||
Text(verbatim: "+\(quests.count - shown.count)")
|
||||
.font(.system(size: 9, weight: .semibold).monospacedDigit())
|
||||
.foregroundStyle(.tertiary)
|
||||
}
|
||||
}
|
||||
.frame(height: headerHeight - rowSpacing, alignment: .top)
|
||||
if quests.isEmpty {
|
||||
Spacer()
|
||||
Text("다짐이 없어요")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity)
|
||||
Spacer()
|
||||
} else {
|
||||
ForEach(shown) { quest in
|
||||
QuestBarRow(quest: quest)
|
||||
.frame(height: rowHeight)
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
.background(WidgetCellBackground())
|
||||
}
|
||||
}
|
||||
|
||||
/// 목표 1개 + 상위 다짐들의 하루 진행률 (중형 구성 2 / 대형 구성 2)
|
||||
struct GoalWithQuestsCellView: View {
|
||||
let goal: GoalSnapshot
|
||||
var questCount = 3
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
GoalBarsCellView(goal: goal)
|
||||
.frame(maxWidth: .infinity)
|
||||
VStack(alignment: .leading, spacing: 7) {
|
||||
Text("다짐 (하루)")
|
||||
.font(.system(size: 9, weight: .semibold))
|
||||
.foregroundStyle(.secondary)
|
||||
if goal.quests.isEmpty {
|
||||
Spacer()
|
||||
Text("다짐이 없어요")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
} else {
|
||||
ForEach(goal.quests.prefix(questCount)) { quest in
|
||||
QuestBarRow(quest: quest)
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
.background(WidgetCellBackground())
|
||||
QuestBarsColumn(quests: goal.quests)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,7 +286,7 @@ struct GoalProgressWidgetView: View {
|
||||
}
|
||||
case .goalWithQuests:
|
||||
if let goal = entry.goals.first ?? nil {
|
||||
GoalWithQuestsCellView(goal: goal, questCount: 3)
|
||||
GoalWithQuestsCellView(goal: goal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -281,7 +309,7 @@ struct GoalProgressWidgetView: View {
|
||||
VStack(spacing: 8) {
|
||||
ForEach(0..<2, id: \.self) { index in
|
||||
if index < entry.goals.count, let goal = entry.goals[index] {
|
||||
GoalWithQuestsCellView(goal: goal, questCount: 3)
|
||||
GoalWithQuestsCellView(goal: goal)
|
||||
} else {
|
||||
WidgetBlankCellView()
|
||||
}
|
||||
@ -292,24 +320,8 @@ struct GoalProgressWidgetView: View {
|
||||
VStack(spacing: 8) {
|
||||
GoalBarsCellView(goal: goal)
|
||||
.frame(maxHeight: 130)
|
||||
VStack(alignment: .leading, spacing: 7) {
|
||||
Text("다짐 (하루)")
|
||||
.font(.system(size: 9, weight: .semibold))
|
||||
.foregroundStyle(.secondary)
|
||||
if goal.quests.isEmpty {
|
||||
Text("다짐이 없어요")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
ForEach(goal.quests.prefix(8)) { quest in
|
||||
QuestBarRow(quest: quest)
|
||||
}
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.padding(10)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
.background(WidgetCellBackground())
|
||||
QuestBarsColumn(quests: goal.quests)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user