Skip to content

Commit ba664f6

Browse files
authored
Merge pull request #109 from YAPP-Github/TNT-310-TrainerViewUpdates
[TNT-310] ํŠธ๋ ˆ์ด๋„ˆ ํ™”๋ฉด UI ๋ณ€๋™ ์‚ฌํ•ญ ๋ฐ˜์˜
2 parents 9cbdea8 + e1bcd34 commit ba664f6

File tree

12 files changed

+662
-75
lines changed

12 files changed

+662
-75
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "icn_check_mark_light_green.svg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "icn_question_mark.svg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Lines changed: 5 additions & 0 deletions
Loading

โ€ŽTnT/Projects/DesignSystem/Sources/Components/PopUp/TPopUpAlertState.swiftโ€Ž

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public struct TPopupAlertState: Equatable {
1717
public var message: String?
1818
/// ํŒ์—…์˜ ๊ฒฝ๊ณ  ์•„์ด์ฝ˜ ํ‘œ์‹œ (์˜ต์…˜)
1919
public var showAlertIcon: Bool
20+
/// ํŒ์—…์— ํ‘œ์‹œํ•  ์ปค์Šคํ…€ ์•„์ด์ฝ˜ (์˜ต์…˜)
21+
public var icon: PopupIcon?
2022
/// ํŒ์—…์— ํ‘œ์‹œ๋  ๋ฒ„ํŠผ ๋ฐฐ์—ด
2123
public var buttons: [ButtonState]
2224

@@ -25,20 +27,42 @@ public struct TPopupAlertState: Equatable {
2527
/// - title: ํŒ์—…์˜ ์ œ๋ชฉ
2628
/// - message: ํŒ์—…์˜ ๋ฉ”์‹œ์ง€ (์„ ํƒ ์‚ฌํ•ญ, ๊ธฐ๋ณธ๊ฐ’: `nil`)
2729
/// - showAlertIcon: ํŒ์—…์˜ ๊ฒฝ๊ณ  ์•„์ด์ฝ˜ ํ‘œ์‹œ (๊ธฐ๋ณธ๊ฐ’: `false`)
30+
/// - icon: ํŒ์—…์— ํ‘œ์‹œํ•  ์ปค์Šคํ…€ ์•„์ด์ฝ˜ (๊ธฐ๋ณธ๊ฐ’: `nil`)
2831
/// - buttons: ํŒ์—…์— ํ‘œ์‹œํ•  ๋ฒ„ํŠผ ๋ฐฐ์—ด (๊ธฐ๋ณธ๊ฐ’: ๋นˆ ๋ฐฐ์—ด)
2932
public init(
3033
title: String,
3134
message: String? = nil,
3235
showAlertIcon: Bool = false,
36+
icon: PopupIcon? = nil,
3337
buttons: [ButtonState] = []
3438
) {
3539
self.title = title
3640
self.message = message
3741
self.showAlertIcon = showAlertIcon
42+
self.icon = icon
3843
self.buttons = buttons
3944
}
4045
}
4146

47+
public extension TPopupAlertState {
48+
enum PopupIcon {
49+
case warning
50+
case checkMarkLightGreen
51+
case questionMark
52+
53+
var imageResource: ImageResource {
54+
switch self {
55+
case .warning:
56+
return .icnWarning
57+
case .checkMarkLightGreen:
58+
return .icnCheckMarkLightGreen
59+
case .questionMark:
60+
return .icnQuestionMark
61+
}
62+
}
63+
}
64+
}
65+
4266
public extension TPopupAlertState {
4367
// TODO: ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ ์™„์„ฑ ์‹œ ์ˆ˜์ •
4468
/// TPopUpAlertView.AlertButton์— ํ‘œ์‹œํ•˜๋Š” ์ •๋ณด์ž…๋‹ˆ๋‹ค.

โ€ŽTnT/Projects/DesignSystem/Sources/Components/PopUp/TPopUpAlertView.swiftโ€Ž

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ public struct TPopUpAlertView: View {
2424
// ํ…์ŠคํŠธ Section
2525
VStack(spacing: 8) {
2626
VStack(spacing: 0) {
27-
if alertState.showAlertIcon {
27+
if let icon = alertState.icon {
28+
Image(icon.imageResource)
29+
.resizable()
30+
.frame(width: 80, height: 80)
31+
} else if alertState.showAlertIcon {
2832
Image(.icnWarning)
2933
.resizable()
3034
.frame(width: 80, height: 80)
31-
} else {
32-
Color.clear
33-
.frame(height: 20)
3435
}
3536
Text(alertState.title)
3637
.typographyStyle(.heading3, with: .neutral900)
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
//
2+
// TBoxTextEditor.swift
3+
// DesignSystem
4+
//
5+
// Created by ๋ฐ•๋ฏผ์„œ on 11/22/25.
6+
// Copyright ยฉ 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
11+
/// ๋ฐ•์Šคํ˜• ํ…Œ๋‘๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปค์Šคํ…€ ํ…์ŠคํŠธ ์—๋””ํ„ฐ์ž…๋‹ˆ๋‹ค.
12+
/// ๊ธฐ์กด TTextEditor์™€ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ƒํƒœ/ํ‘ธํ„ฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
13+
public struct TBoxTextEditor: View {
14+
15+
/// TextEditor ์ˆ˜ํ‰ ํŒจ๋”ฉ ๊ฐ’
16+
private static let horizontalPadding: CGFloat = 16
17+
/// TextEditor ์ˆ˜์ง ํŒจ๋”ฉ ๊ฐ’
18+
private static let verticalPadding: CGFloat = 12
19+
/// TextEditor ๊ธฐ๋ณธ ๋†’์ด๊ฐ’
20+
public static let defaultHeight: CGFloat = 40
21+
22+
/// ํ•˜๋‹จ์— ํ‘œ์‹œ๋˜๋Š” ํ‘ธํ„ฐ ๋ทฐ
23+
private let footer: Footer?
24+
/// Placeholder ํ…์ŠคํŠธ
25+
private let placeholder: String
26+
/// ํ…์ŠคํŠธ ์—๋””ํ„ฐ ์‚ฌ์ด์ฆˆ
27+
private let size: Size
28+
/// ํ…์ŠคํŠธ ํ•„๋“œ ์ƒํƒœ
29+
@Binding private var status: Status
30+
/// ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ
31+
@Binding private var text: String
32+
33+
/// ๋‚ด๋ถ€์—์„œ ๋™์ ์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” ํ…์ŠคํŠธ ์—๋””ํ„ฐ ๋†’์ด
34+
@State private var textHeight: CGFloat = defaultHeight
35+
/// ํ…์ŠคํŠธ ์—๋””ํ„ฐ ํฌ์ปค์Šค ์ƒํƒœ
36+
@FocusState var isFocused: Bool
37+
38+
/// TBoxTextEditor ์ƒ์„ฑ์ž
39+
/// - Parameters:
40+
/// - placeholder: Placeholder ํ…์ŠคํŠธ (๊ธฐ๋ณธ๊ฐ’: "๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”").
41+
/// - size: ํ…์ŠคํŠธ ์—๋””ํ„ฐ ์‚ฌ์ด์ฆˆ.
42+
/// - text: ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ”์ธ๋”ฉ.
43+
/// - textEditorStatus: ํ…์ŠคํŠธ ์—๋””ํ„ฐ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ”์ธ๋”ฉ.
44+
/// - footer: TextEditor ํ•˜๋‹จ์— ํ‘œ์‹œ๋  `TBoxTextEditor.Footer`๋ฅผ ์ •์˜ํ•˜๋Š” ํด๋กœ์ €.
45+
public init(
46+
placeholder: String = "๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”",
47+
size: Size = .large,
48+
text: Binding<String>,
49+
textEditorStatus: Binding<Status>,
50+
footer: () -> Footer? = { nil }
51+
) {
52+
self.placeholder = placeholder
53+
self.size = size
54+
self._text = text
55+
self._status = textEditorStatus
56+
self.footer = footer()
57+
}
58+
59+
public var body: some View {
60+
VStack(alignment: .leading, spacing: 8) {
61+
ZStack(alignment: .topLeading) {
62+
TextEditor(text: $text)
63+
.autocorrectionDisabled()
64+
.scrollDisabled(true)
65+
.focused($isFocused)
66+
.font(Typography.FontStyle.body1Medium.font)
67+
.lineSpacing(Typography.FontStyle.body1Medium.lineSpacing)
68+
.kerning(Typography.FontStyle.body1Medium.letterSpacing)
69+
.foregroundColor(status.textColor)
70+
.tint(Color.neutral800)
71+
.frame(minHeight: textHeight, maxHeight: .infinity)
72+
.padding(.vertical, TBoxTextEditor.verticalPadding)
73+
.padding(.horizontal, TBoxTextEditor.horizontalPadding)
74+
.background(Color.common0)
75+
.scrollContentBackground(.hidden)
76+
.cornerRadius(8)
77+
.overlay(
78+
RoundedRectangle(cornerRadius: 8)
79+
.stroke(status.borderColor(isFocused: isFocused), lineWidth: 1)
80+
)
81+
.frame(height: size.height)
82+
83+
if text.isEmpty {
84+
Text(placeholder)
85+
.typographyStyle(.body1Medium, with: .neutral400)
86+
.padding(.vertical, TBoxTextEditor.verticalPadding + 8)
87+
.padding(.horizontal, TBoxTextEditor.horizontalPadding + 4)
88+
}
89+
}
90+
if let footer {
91+
footer
92+
}
93+
}
94+
}
95+
}
96+
97+
public extension TBoxTextEditor {
98+
/// TBoxTextEditor์˜ Footer์ž…๋‹ˆ๋‹ค
99+
struct Footer: View {
100+
/// ์ตœ๋Œ€ ์ž…๋ ฅ ๊ฐ€๋Šฅ ๊ธ€์ž ์ˆ˜
101+
private let textLimit: Int
102+
/// ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ ์นด์šดํŠธ
103+
private var textCount: Int
104+
/// ๊ฒฝ๊ณ  ํ…์ŠคํŠธ
105+
private var warningText: String
106+
/// ํ…์ŠคํŠธ ํ•„๋“œ ์ƒํƒœ
107+
@Binding private var status: Status
108+
109+
/// Footer ์ƒ์„ฑ์ž
110+
/// - Parameters:
111+
/// - textLimit: ์ตœ๋Œ€ ์ž…๋ ฅ ๊ฐ€๋Šฅ ๊ธ€์ž ์ˆ˜.
112+
/// - status: ํ…์ŠคํŠธ ์—๋””ํ„ฐ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ”์ธ๋”ฉ.
113+
/// - textCount: ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ ๊ธ€์ž ์ˆ˜.
114+
public init(
115+
textLimit: Int,
116+
status: Binding<Status>,
117+
textCount: Int,
118+
warningText: String = "๊ธ€์ž ์ˆ˜๋ฅผ ์ดˆ๊ณผํ–ˆ์–ด์š”"
119+
) {
120+
self.textLimit = textLimit
121+
self.textCount = textCount
122+
self._status = status
123+
self.warningText = warningText
124+
}
125+
126+
public var body: some View {
127+
HStack {
128+
if status == .invalid {
129+
Text(warningText)
130+
.typographyStyle(.label2Medium, with: status.footerColor)
131+
}
132+
Spacer()
133+
Text("\(textCount)/\(textLimit)")
134+
.typographyStyle(.label2Medium, with: status.footerColor)
135+
}
136+
}
137+
}
138+
}
139+
140+
public extension TBoxTextEditor {
141+
/// TextEditor์— ํ‘œ์‹œ๋˜๋Š” ์ƒํƒœ์ž…๋‹ˆ๋‹ค
142+
enum Status {
143+
case empty
144+
case filled
145+
case invalid
146+
147+
/// ํ…Œ๋‘๋ฆฌ ์ƒ‰์ƒ ์„ค์ •
148+
func borderColor(isFocused: Bool) -> Color {
149+
switch self {
150+
case .empty:
151+
return isFocused ? .neutral600 : .neutral300
152+
case .filled:
153+
return isFocused ? .neutral600 : .neutral300
154+
case .invalid:
155+
return .red500
156+
}
157+
}
158+
159+
/// ํ…์ŠคํŠธ ์ƒ‰์ƒ ์„ค์ •
160+
var textColor: Color {
161+
switch self {
162+
case .empty:
163+
return .neutral400
164+
case .filled, .invalid:
165+
return .neutral600
166+
}
167+
}
168+
169+
/// ํ‘ธํ„ฐ ์ƒ‰์ƒ ์„ค์ •
170+
var footerColor: Color {
171+
switch self {
172+
case .empty, .filled:
173+
return .neutral300
174+
case .invalid:
175+
return .red500
176+
}
177+
}
178+
}
179+
180+
/// TextEditor์˜ ํฌ๊ธฐ
181+
enum Size {
182+
case small
183+
case large
184+
185+
/// ๋†’์ด
186+
var height: CGFloat {
187+
switch self {
188+
case .small:
189+
return 52
190+
case .large:
191+
return 130
192+
}
193+
}
194+
}
195+
}

0 commit comments

Comments
ย (0)