Skip to content

Commit 8a41354

Browse files
authored
Merge branch 'develop' into TNT-205-alarmPage
2 parents 84c88a6 + 9dcef4f commit 8a41354

File tree

29 files changed

+1257
-65
lines changed

29 files changed

+1257
-65
lines changed

TnT/Projects/DesignSystem/Resources/Assets.xcassets/Icons/icn_clock.imageset/Size=16, Type=clock.svg

Lines changed: 2 additions & 2 deletions
Loading

TnT/Projects/DesignSystem/Resources/Assets.xcassets/Icons/icn_start_empty.imageset/Contents.json renamed to TnT/Projects/DesignSystem/Resources/Assets.xcassets/Icons/icn_star.imageset/Contents.json

File renamed without changes.

TnT/Projects/DesignSystem/Resources/Assets.xcassets/Icons/icn_start_empty.imageset/Size=16, Type=star.svg renamed to TnT/Projects/DesignSystem/Resources/Assets.xcassets/Icons/icn_star.imageset/Size=16, Type=star.svg

Lines changed: 1 addition & 1 deletion
Loading

TnT/Projects/DesignSystem/Resources/Assets.xcassets/Icons/icn_star_smile.imageset/Contents.json

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,7 @@
22
"images" : [
33
{
44
"filename" : "Size=24, Type=smile.svg",
5-
"idiom" : "universal",
6-
"scale" : "1x"
7-
},
8-
{
9-
"idiom" : "universal",
10-
"scale" : "2x"
11-
},
12-
{
13-
"idiom" : "universal",
14-
"scale" : "3x"
5+
"idiom" : "universal"
156
}
167
],
178
"info" : {

TnT/Projects/DesignSystem/Resources/Assets.xcassets/Icons/icn_star_smile.imageset/Size=24, Type=smile.svg

Lines changed: 4 additions & 4 deletions
Loading
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// AutoSizingBottomSheetModifier.swift
3+
// DesignSystem
4+
//
5+
// Created by 박민서 on 2/2/25.
6+
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
11+
/// 바텀시트의 높이를 자동 조정하는 ViewModifier
12+
/// 내부 컨텐츠의 크기를 측정하여 적절한 높이를 설정합니다
13+
struct AutoSizingBottomSheetModifier: ViewModifier {
14+
/// 바텀시트 상단의 그래버 표시
15+
let presentationDragIndicator: Visibility
16+
/// 측정된 컨텐츠의 높이 (초기값 300)
17+
@State private var contentHeight: CGFloat = 300
18+
19+
init(presentationDragIndicator: Visibility = .visible) {
20+
self.presentationDragIndicator = presentationDragIndicator
21+
}
22+
23+
func body(content: Content) -> some View {
24+
content
25+
.background(
26+
GeometryReader { proxy in
27+
Color.clear
28+
.onAppear {
29+
contentHeight = proxy.size.height + 50
30+
}
31+
.onChange(of: proxy.size.height) { _, newHeight in
32+
contentHeight = newHeight + 50
33+
}
34+
}
35+
)
36+
.presentationDetents([.height(contentHeight)])
37+
.presentationDragIndicator(presentationDragIndicator)
38+
}
39+
}
40+
41+
public extension View {
42+
/// 뷰에 자동 크기 조정 바텀시트를 적용하는 Modifier
43+
/// - Parameter presentationDragIndicator: 바텀시트 상단 Grabber의 가시성 설정 (기본값: .visible)
44+
/// - Returns: 자동 크기 조정 바텀시트가 적용된 뷰
45+
func autoSizingBottomSheet(presentationDragIndicator: Visibility = .visible) -> some View {
46+
self.modifier(AutoSizingBottomSheetModifier(presentationDragIndicator: presentationDragIndicator))
47+
}
48+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//
2+
// TCalendarCell.swift
3+
// DesignSystem
4+
//
5+
// Created by 박민서 on 2/1/25.
6+
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import FSCalendar
10+
11+
/// TCalendar에 사용되는 Cell 입니다
12+
final class TCalendarCell: FSCalendarCell {
13+
// MARK: Properties
14+
static let identifier: String = "TCalendarCell"
15+
static let cellSize: CGSize = CGSize(width: 51, height: 54)
16+
17+
/// Cell에 표시되는 날짜
18+
private var customDate: Date?
19+
/// Cell 이 선택되었는지 표시
20+
private var isCellSelected: Bool = false
21+
/// Cell 스타일
22+
private var style: Style = .default
23+
/// Cell에 표시되는 일정 카운트
24+
private var eventCount: Int = 0
25+
/// 주간/월간 모드인지 표시
26+
private var isWeekMode: Bool = false
27+
28+
// MARK: UI Elements
29+
private let dayLabel: UILabel = UILabel()
30+
private let eventStackView: UIStackView = UIStackView()
31+
private let eventIcon: UIImageView = UIImageView()
32+
private let eventCountLabel: UILabel = UILabel()
33+
private let backgroundContainer: UIView = UIView()
34+
35+
override init(frame: CGRect) {
36+
super.init(frame: frame)
37+
setupUI()
38+
setUpHierarchy()
39+
setUpConstraint()
40+
}
41+
42+
required init(coder aDecoder: NSCoder) {
43+
fatalError("init(coder:) has not been implemented")
44+
}
45+
46+
private func setupUI() {
47+
backgroundContainer.layer.cornerRadius = 8
48+
49+
dayLabel.font = Typography.FontStyle.body2Medium.uiFont
50+
dayLabel.textAlignment = .center
51+
52+
eventStackView.axis = .horizontal
53+
eventStackView.spacing = 2
54+
eventStackView.alignment = .center
55+
56+
eventIcon.image = UIImage(resource: .icnStar).withRenderingMode(.alwaysTemplate)
57+
eventIcon.tintColor = UIColor(.red300)
58+
eventIcon.contentMode = .scaleAspectFit
59+
eventIcon.frame = CGRect(x: 0, y: 0, width: 12, height: 12)
60+
61+
eventCountLabel.font = Typography.FontStyle.label2Medium.uiFont
62+
eventCountLabel.textColor = UIColor(.neutral400)
63+
}
64+
65+
private func setUpHierarchy() {
66+
eventStackView.addArrangedSubview(eventIcon)
67+
eventStackView.addArrangedSubview(eventCountLabel)
68+
69+
contentView.addSubview(backgroundContainer)
70+
contentView.addSubview(dayLabel)
71+
contentView.addSubview(eventStackView)
72+
}
73+
74+
private func setUpConstraint() {
75+
dayLabel.translatesAutoresizingMaskIntoConstraints = false
76+
eventStackView.translatesAutoresizingMaskIntoConstraints = false
77+
backgroundContainer.translatesAutoresizingMaskIntoConstraints = false
78+
79+
NSLayoutConstraint.activate([
80+
dayLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
81+
dayLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
82+
dayLabel.widthAnchor.constraint(equalToConstant: 32),
83+
dayLabel.heightAnchor.constraint(equalToConstant: 32),
84+
85+
backgroundContainer.centerXAnchor.constraint(equalTo: dayLabel.centerXAnchor),
86+
backgroundContainer.centerYAnchor.constraint(equalTo: dayLabel.centerYAnchor),
87+
backgroundContainer.widthAnchor.constraint(equalTo: dayLabel.widthAnchor),
88+
backgroundContainer.heightAnchor.constraint(equalTo: dayLabel.heightAnchor),
89+
90+
eventStackView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
91+
eventStackView.topAnchor.constraint(equalTo: dayLabel.bottomAnchor, constant: 4)
92+
])
93+
}
94+
95+
/// 셀 스타일 표시 업데이트
96+
private func updateAppearance() {
97+
dayLabel.textColor = style.textColor
98+
backgroundContainer.backgroundColor = style.backgroundColor
99+
}
100+
101+
/// 일정 카운트 표시 업데이트
102+
private func updateEventDisplay() {
103+
eventCountLabel.text = "\(eventCount)"
104+
let eventExists: Bool = eventCount > 0
105+
eventStackView.isHidden = !eventExists
106+
let presentCount: Bool = !isWeekMode && eventExists
107+
eventCountLabel.isHidden = !presentCount
108+
}
109+
110+
override func prepareForReuse() {
111+
super.prepareForReuse()
112+
// 날짜 및 선택 상태 초기화
113+
customDate = nil
114+
isCellSelected = false
115+
isWeekMode = false
116+
117+
// 일정 관련 초기화
118+
eventCount = 0
119+
eventStackView.isHidden = true
120+
eventCountLabel.text = nil
121+
122+
// 스타일 초기화
123+
style = .default
124+
updateAppearance()
125+
updateEventDisplay()
126+
}
127+
}
128+
129+
extension TCalendarCell {
130+
/// 셀 설정
131+
func configure(
132+
with date: Date,
133+
isCellSelected: Bool,
134+
eventCount: Int = 0,
135+
isWeekMode: Bool = false
136+
) {
137+
self.customDate = date
138+
self.isCellSelected = isCellSelected
139+
self.eventCount = eventCount
140+
self.isWeekMode = isWeekMode
141+
142+
// 현재 날짜 및 선택 상태를 반영, Style 설정
143+
if isCellSelected {
144+
self.style = .selected
145+
} else if Calendar.current.isDateInToday(date) {
146+
self.style = .today
147+
} else {
148+
self.style = .default
149+
}
150+
151+
dayLabel.text = "\(Calendar.current.component(.day, from: date))"
152+
self.updateAppearance()
153+
self.updateEventDisplay()
154+
}
155+
}
156+
157+
extension TCalendarCell {
158+
enum Style {
159+
case `default`
160+
case today
161+
case selected
162+
163+
var textColor: UIColor {
164+
switch self {
165+
case .default, .today:
166+
return UIColor(.neutral600)
167+
case .selected:
168+
return UIColor(.common0)
169+
}
170+
}
171+
172+
var backgroundColor: UIColor {
173+
switch self {
174+
case .default:
175+
return .clear
176+
case .today:
177+
return UIColor(.neutral200)
178+
case .selected:
179+
return UIColor(.neutral900)
180+
}
181+
}
182+
}
183+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// TCalendarHeader.swift
3+
// DesignSystem
4+
//
5+
// Created by 박민서 on 2/2/25.
6+
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
11+
/// TCalendarView의 헤더입니다
12+
/// 월 이동 로직을 추가합니다
13+
public struct TCalendarHeader<RightView: View>: View {
14+
15+
@Binding private var currentPage: Date
16+
private var formatter: (Date) -> String
17+
private var rightView: (() -> RightView)?
18+
19+
public init(
20+
currentPage: Binding<Date>,
21+
formatter: @escaping (Date) -> String,
22+
rightView: (() -> RightView)? = nil
23+
) {
24+
self._currentPage = currentPage
25+
self.formatter = formatter
26+
self.rightView = rightView
27+
}
28+
29+
public var body: some View {
30+
HStack(spacing: 0) {
31+
Button(action: {
32+
movePage(-1)
33+
}, label: {
34+
Image(.icnTriangleLeft32px)
35+
.resizable()
36+
.frame(width: 32, height: 32)
37+
})
38+
39+
Text("\(formatter(currentPage))")
40+
.typographyStyle(.heading3, with: .neutral900)
41+
42+
Button(action: {
43+
movePage(1)
44+
}, label: {
45+
Image(.icnTriangleRight32px)
46+
.resizable()
47+
.frame(width: 32, height: 32)
48+
})
49+
}
50+
.frame(maxWidth: .infinity)
51+
.overlay(alignment: .trailing) {
52+
rightView?()
53+
}
54+
.padding(.vertical, 8)
55+
.padding(.horizontal, 20)
56+
}
57+
58+
private func movePage(_ direction: Int) {
59+
if let nextPage = Calendar.current.date(byAdding: .month, value: direction, to: currentPage) {
60+
currentPage = nextPage
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)