88import SwiftUI
99
1010struct HeatmapView : View {
11- @Environment ( \. safeAreaInsets) private var safeAreaInsets
12- @Environment ( \. sceneWidth) private var sceneWidth
11+ @State private var availableWidth = CGFloat . zero
1312 let quarter : HeatmapQuarter
1413 let selectedActivityKinds : Set < ActivityKind >
1514 let selectedDay : HeatmapDay ?
@@ -21,31 +20,32 @@ struct HeatmapView: View {
2120 weekCounts: quarter. months. map ( \. weeks. count)
2221 )
2322
24- HStack ( alignment: . top, spacing: layout. monthSpacing) {
25- ForEach ( quarter. months) { month in
26- MonthCompactHeatmapView (
27- month: month,
28- maxCount: maxCount,
29- layout: layout,
30- selectedActivityKinds: selectedActivityKinds,
31- selectedDay: selectedDay,
32- onSelectDay: onSelectDay
33- )
23+ ScrollView ( . horizontal) {
24+ LazyHStack ( alignment: . top, spacing: layout. monthSpacing) {
25+ ForEach ( quarter. months) { month in
26+ MonthCompactHeatmapView (
27+ month: month,
28+ maxCount: maxCount,
29+ layout: layout,
30+ selectedActivityKinds: selectedActivityKinds,
31+ selectedDay: selectedDay,
32+ onSelectDay: onSelectDay
33+ )
34+ }
35+ }
36+ . padding ( . vertical, 2 )
37+ . frame ( width: layout. contentWidth, alignment: . leading)
38+ }
39+ . scrollIndicators ( . hidden)
40+ . scrollDisabled ( true )
41+ . frame ( maxWidth: . infinity, alignment: . leading)
42+ . background {
43+ GeometryReader { geometry in
44+ Color . clear
45+ . onAppear { updateAvailableWidth ( geometry. size. width) }
46+ . onChange ( of: geometry. size. width) { updateAvailableWidth ( $1) }
3447 }
3548 }
36- . padding ( . vertical, 2 )
37- }
38-
39- private var availableWidth : CGFloat {
40- // ProfileView의 바깥 가로 패딩(16)과 히트맵 카드 내부 패딩(12)을 합한 값
41- let horizontalPadding : CGFloat = 16 + 12
42- return max (
43- 0 ,
44- sceneWidth
45- - safeAreaInsets. leading
46- - safeAreaInsets. trailing
47- - ( horizontalPadding * 2 )
48- )
4949 }
5050
5151 private var maxCount : Int {
@@ -70,13 +70,22 @@ struct HeatmapView: View {
7070 }
7171 return value
7272 }
73+
74+ private func updateAvailableWidth( _ width: CGFloat ) {
75+ if availableWidth != width {
76+ availableWidth = width
77+ }
78+ }
7379}
7480
7581private struct HeatmapLayout {
82+ private static let minimumCellSize : CGFloat = 8
83+ private static let maximumCellSize : CGFloat = 22
7684 let cellSize : CGFloat
7785 let cellSpacing : CGFloat = 4
7886 let monthSpacing : CGFloat = 12
7987 let monthTitleSpacing : CGFloat = 6
88+ let contentWidth : CGFloat
8089
8190 init ( availableWidth: CGFloat , weekCounts: [ Int ] ) {
8291 let totalColumns = max ( weekCounts. reduce ( 0 , + ) , 1 )
@@ -85,7 +94,9 @@ private struct HeatmapLayout {
8594 }
8695 let fixedWidth = monthSpacing * CGFloat( max ( weekCounts. count - 1 , 0 ) )
8796 + cellSpacing * CGFloat( totalColumnSpacings)
88- cellSize = max ( 0 , availableWidth - fixedWidth) / CGFloat( totalColumns)
97+ let fittingCellSize = max ( 0 , availableWidth - fixedWidth) / CGFloat( totalColumns)
98+ cellSize = min ( max ( fittingCellSize, Self . minimumCellSize) , Self . maximumCellSize)
99+ contentWidth = cellSize * CGFloat( totalColumns) + fixedWidth
89100 }
90101
91102 var cellCornerRadius : CGFloat {
0 commit comments