-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TNT-139] TControlButton, TPopUp 컴포넌트 코드 작성 #21
Merged
FpRaArNkK
merged 6 commits into
develop
from
TNT-139-createMinComponents-ControlAndPopUp
Jan 19, 2025
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
3291b3a
[Feat] ControlButton 작성
FpRaArNkK 107bcc3
[Feat] TPopUp 컴포넌트 작성
FpRaArNkK 4c42ede
[Feat] TControl에서 Toggle 분리 -> TToggleStyle
FpRaArNkK be9b1c4
[Refactor] TPopUp 컴포넌트 TCA 고려한 구조로 리팩토링
FpRaArNkK 5a7525e
[Delete] 불필요 코드 제거
FpRaArNkK c0683f4
[Chore] 불필요 로직 삭제, 접근제어자 추가
FpRaArNkK File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
74 changes: 74 additions & 0 deletions
74
TnT/Projects/DesignSystem/Sources/Components/Control/TControlButton.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// | ||
// TControlButton.swift | ||
// DesignSystem | ||
// | ||
// Created by 박민서 on 1/15/25. | ||
// Copyright © 2025 yapp25thTeamTnT. All rights reserved. | ||
// | ||
|
||
import SwiftUI | ||
|
||
/// TnT 앱 내에서 전반적으로 사용되는 커스텀 컨트롤 버튼 컴포넌트입니다. | ||
public struct TControlButton: View { | ||
/// 버튼 기본 사이즈 | ||
static private let defaultSize: CGSize = .init(width: 24, height: 24) | ||
/// 버튼 탭 액션 | ||
private let tapAction: () -> Void | ||
/// 버튼 스타일 | ||
private let type: Style | ||
/// 버튼 선택 상태 | ||
@Binding private var isSelected: Bool | ||
|
||
/// TControlButton 생성자 | ||
/// - Parameters: | ||
/// - type: 버튼의 스타일. `TControlButton.Style` 사용. | ||
/// - isSelected: 버튼의 선택 상태를 관리하는 바인딩. | ||
/// - action: 버튼이 탭되었을 때 실행할 액션. (기본값: 빈 클로저) | ||
public init( | ||
type: Style, | ||
isSelected: Binding<Bool>, | ||
action: @escaping () -> Void = {} | ||
) { | ||
self.type = type | ||
self._isSelected = isSelected | ||
self.tapAction = action | ||
} | ||
|
||
public var body: some View { | ||
Button(action: { | ||
tapAction() | ||
}, label: { | ||
type.image(isSelected: isSelected) | ||
.resizable() | ||
.scaledToFit() | ||
.frame(width: TControlButton.defaultSize.width, height: TControlButton.defaultSize.height) | ||
}) | ||
} | ||
} | ||
|
||
public extension TControlButton { | ||
/// TControlButton의 스타일입니다. | ||
enum Style { | ||
case radio | ||
case checkMark | ||
case checkbox | ||
case star | ||
case heart | ||
|
||
/// 선택 상태에 따른 이미지 반환 | ||
func image(isSelected: Bool) -> Image { | ||
switch self { | ||
case .radio: | ||
return Image(isSelected ? .icnRadioButtonSelected : .icnRadioButtonUnselected) | ||
case .checkMark: | ||
return Image(isSelected ? .icnCheckMarkFilled : .icnCheckMarkEmpty) | ||
case .checkbox: | ||
return Image(isSelected ? .icnCheckButtonSelected : .icnCheckButtonUnselected) | ||
case .star: | ||
return Image(isSelected ? .icnStarFilled : .icnStarEmpty) | ||
case .heart: | ||
return Image(isSelected ? .icnHeartFilled : .icnHeartEmpty) | ||
} | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
TnT/Projects/DesignSystem/Sources/Components/Control/TToggleStyle.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// | ||
// TToggleStyle.swift | ||
// DesignSystem | ||
// | ||
// Created by 박민서 on 1/15/25. | ||
// Copyright © 2025 yapp25thTeamTnT. All rights reserved. | ||
// | ||
|
||
import SwiftUI | ||
|
||
/// TToggleStyle: ViewModifier | ||
/// SwiftUI의 `Toggle`에 TnT 스타일을 적용하기 위한 커스텀 ViewModifier입니다. | ||
/// 기본 크기와 토글 스타일을 설정하여 재사용 가능한 스타일링을 제공합니다. | ||
struct TToggleStyle: ViewModifier { | ||
/// 기본 토글 크기 | ||
static let defaultSize: CGSize = .init(width: 44, height: 24) | ||
|
||
/// `ViewModifier`가 적용된 뷰의 구성 | ||
/// - Parameter content: 스타일이 적용될 뷰 | ||
/// - Returns: TnT 스타일이 적용된 뷰 | ||
func body(content: Content) -> some View { | ||
content | ||
.toggleStyle(SwitchToggleStyle(tint: .red500)) | ||
.labelsHidden() | ||
.frame(width: TToggleStyle.defaultSize.width, height: TToggleStyle.defaultSize.height) | ||
} | ||
} | ||
|
||
/// Toggle 확장: TnT 스타일 적용 | ||
public extension Toggle { | ||
/// SwiftUI의 기본 `Toggle`에 TnT 스타일을 적용합니다. | ||
func applyTToggleStyle() -> some View { | ||
self.modifier(TToggleStyle()) | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
TnT/Projects/DesignSystem/Sources/Components/PopUp/TPopUpAlertState.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// | ||
// TPopupAlertState.swift | ||
// DesignSystem | ||
// | ||
// Created by 박민서 on 1/15/25. | ||
// Copyright © 2025 yapp25thTeamTnT. All rights reserved. | ||
// | ||
|
||
import SwiftUI | ||
import ComposableArchitecture | ||
|
||
/// TPopUpAlertView에 표시하는 정보입니다. | ||
/// 팝업의 제목, 메시지, 버튼 정보를 포함. | ||
public struct TPopupAlertState: Equatable { | ||
/// 팝업 제목 | ||
public var title: String | ||
/// 팝업 메시지 (옵션) | ||
public var message: String? | ||
/// 팝업에 표시될 버튼 배열 | ||
public var buttons: [ButtonState] | ||
|
||
/// TPopupAlertState 초기화 메서드 | ||
/// - Parameters: | ||
/// - title: 팝업의 제목 | ||
/// - message: 팝업의 메시지 (선택 사항, 기본값: `nil`) | ||
/// - buttons: 팝업에 표시할 버튼 배열 (기본값: 빈 배열) | ||
public init( | ||
title: String, | ||
message: String? = nil, | ||
buttons: [ButtonState] = [] | ||
) { | ||
self.title = title | ||
self.message = message | ||
self.buttons = buttons | ||
} | ||
} | ||
|
||
public extension TPopupAlertState { | ||
// TODO: 버튼 컴포넌트 완성 시 수정 | ||
/// TPopUpAlertView.AlertButton에 표시하는 정보입니다. | ||
struct ButtonState: Equatable { | ||
/// 버튼 제목 | ||
public let title: String | ||
/// 버튼 스타일 | ||
public let style: Style | ||
/// 버튼 클릭 시 동작 | ||
public let action: EquatableClosure | ||
|
||
public enum Style { | ||
case primary | ||
case secondary | ||
} | ||
|
||
/// TPopupAlertState.ButtonState 초기화 메서드 | ||
/// - Parameters: | ||
/// - title: 버튼 제목 | ||
/// - style: 버튼 스타일 (기본값: `.primary`) | ||
/// - action: 버튼 클릭 시 동작 | ||
public init( | ||
title: String, | ||
style: Style = .primary, | ||
action: EquatableClosure | ||
) { | ||
self.action = action | ||
self.title = title | ||
self.style = style | ||
} | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
TnT/Projects/DesignSystem/Sources/Components/PopUp/TPopUpAlertView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// | ||
// TPopUpAlertView.swift | ||
// DesignSystem | ||
// | ||
// Created by 박민서 on 1/16/25. | ||
// Copyright © 2025 yapp25thTeamTnT. All rights reserved. | ||
// | ||
|
||
import SwiftUI | ||
|
||
/// 팝업 Alert의 콘텐츠 뷰 | ||
/// 타이틀, 메시지, 버튼 섹션으로 구성. | ||
public struct TPopUpAlertView: View { | ||
/// 팝업 상태 정보 | ||
private let alertState: TPopupAlertState | ||
|
||
/// - Parameter alertState: 팝업에 표시할 상태 정보 | ||
public init(alertState: TPopupAlertState) { | ||
self.alertState = alertState | ||
} | ||
|
||
public var body: some View { | ||
VStack(spacing: 20) { | ||
// 텍스트 Section | ||
VStack(spacing: 8) { | ||
Text(alertState.title) | ||
.typographyStyle(.heading4, with: .neutral900) | ||
.multilineTextAlignment(.center) | ||
.padding(.top, 20) | ||
if let message = alertState.message { | ||
Text(message) | ||
.typographyStyle(.body2Medium, with: .neutral500) | ||
.multilineTextAlignment(.center) | ||
} | ||
} | ||
|
||
// 버튼 Section | ||
HStack { | ||
ForEach(alertState.buttons, id: \.title) { buttonState in | ||
buttonState.toButton() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
public extension TPopUpAlertView { | ||
// TODO: 버튼 컴포넌트 완성 시 수정 | ||
struct AlertButton: View { | ||
let title: String | ||
let style: TPopupAlertState.ButtonState.Style | ||
let action: () -> Void | ||
|
||
init( | ||
title: String, | ||
style: TPopupAlertState.ButtonState.Style, | ||
action: @escaping () -> Void | ||
) { | ||
self.title = title | ||
self.style = style | ||
self.action = action | ||
} | ||
|
||
public var body: some View { | ||
Button(action: action) { | ||
Text(title) | ||
.typographyStyle(.body1Medium, with: style == .primary ? Color.neutral50 : Color.neutral500) | ||
.padding() | ||
.frame(maxWidth: .infinity) | ||
.background(style == .primary ? Color.neutral900 : Color.neutral100) | ||
.foregroundColor(.white) | ||
.cornerRadius(8) | ||
} | ||
} | ||
} | ||
} | ||
|
||
public extension TPopupAlertState.ButtonState { | ||
/// `ButtonState`를 `AlertButton`으로 변환 | ||
func toButton() -> TPopUpAlertView.AlertButton { | ||
TPopUpAlertView.AlertButton( | ||
title: self.title, | ||
style: self.style, | ||
action: self.action.execute | ||
) | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
TnT/Projects/DesignSystem/Sources/Components/PopUp/TPopUpModifier.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// | ||
// TPopUpModifier.swift | ||
// DesignSystem | ||
// | ||
// Created by 박민서 on 1/16/25. | ||
// Copyright © 2025 yapp25thTeamTnT. All rights reserved. | ||
// | ||
|
||
import SwiftUI | ||
|
||
/// 팝업 컨테이너 (공통 레이아웃) | ||
public struct TPopUpModifier<InnerContent: View>: ViewModifier { | ||
|
||
/// 팝업 내부 콘텐츠의 기본 패딩 | ||
private let defaultInnerPadding: CGFloat = 20 | ||
/// 팝업 배경의 기본 불투명도 | ||
/// 0.0 = 완전 투명, 1.0 = 완전 불투명 | ||
private let defaultBackgroundOpacity: Double = 0.8 | ||
/// 팝업의 기본 배경 색상 | ||
private let defaultPopUpBackgroundColor: Color = .white | ||
/// 팝업 모서리의 기본 곡률 (Corner Radius) | ||
private let defaultCornerRadius: CGFloat = 16 | ||
/// 팝업 그림자의 기본 반경 | ||
private let defaultShadowRadius: CGFloat = 10 | ||
/// 팝업 콘텐츠의 기본 크기 | ||
private let defaultContentSize: CGSize = .init(width: 297, height: 175) | ||
|
||
/// 팝업에 표시될 내부 콘텐츠 클로저 | ||
private let innerContent: () -> InnerContent | ||
/// 팝업 표시 여부 | ||
@Binding private var isPresented: Bool | ||
|
||
/// TPopupModifier 초기화 메서드 | ||
/// - Parameters: | ||
/// - isPresented: 팝업 표시 여부를 제어하는 Binding | ||
/// - newContent: 팝업에 표시될 내부 콘텐츠 클로저 | ||
public init( | ||
isPresented: Binding<Bool>, | ||
newContent: @escaping () -> InnerContent | ||
) { | ||
self._isPresented = isPresented | ||
self.innerContent = newContent | ||
} | ||
|
||
public func body(content: Content) -> some View { | ||
ZStack { | ||
// 기존 뷰 | ||
content | ||
.zIndex(0) | ||
|
||
if isPresented { | ||
// 반투명 배경 | ||
Color.black.opacity(defaultBackgroundOpacity) | ||
.ignoresSafeArea() | ||
.zIndex(1) | ||
.onTapGesture { | ||
isPresented = false | ||
} | ||
|
||
// 팝업 뷰 | ||
self.innerContent() | ||
.frame(minWidth: defaultContentSize.width, minHeight: defaultContentSize.height) | ||
.padding(defaultInnerPadding) | ||
.background(defaultPopUpBackgroundColor) | ||
.cornerRadius(defaultCornerRadius) | ||
.shadow(radius: defaultShadowRadius) | ||
.padding() | ||
.zIndex(2) | ||
} | ||
} | ||
.animation(.easeInOut, value: isPresented) | ||
} | ||
} | ||
|
||
public extension View { | ||
/// 팝업 표시를 위한 View Modifier | ||
/// - Parameters: | ||
/// - isPresented: 팝업 표시 여부를 제어하는 Binding | ||
/// - content: 팝업 내부에 표시할 콘텐츠 클로저 | ||
/// - Returns: 팝업이 추가된 View | ||
func tPopUp<Content: View>( | ||
isPresented: Binding<Bool>, | ||
@ViewBuilder content: @escaping () -> Content | ||
) -> some View { | ||
self.modifier(TPopUpModifier<Content>(isPresented: isPresented, newContent: content)) | ||
} | ||
|
||
/// `TPopUp.Alert` 팝업 전용 View Modifier | ||
/// - Parameters: | ||
/// - isPresented: 팝업 표시 여부를 제어하는 Binding | ||
/// - content: 팝업 알림 내용을 구성하는 클로저 | ||
/// - Returns: 팝업이 추가된 View | ||
func tPopUp(isPresented: Binding<Bool>, content: @escaping () -> TPopUpAlertView) -> some View { | ||
self.modifier(TPopUpModifier(isPresented: isPresented, newContent: content)) | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
TnT/Projects/DesignSystem/Sources/Utility/EquatableClosure.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// | ||
// EquatableClosure.swift | ||
// DesignSystem | ||
// | ||
// Created by 박민서 on 1/16/25. | ||
// Copyright © 2025 yapp25thTeamTnT. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// 클로저를 Equatable로 사용할 수 있도록 래핑한 구조체입니다. | ||
/// 고유 ID를 통해 클로저의 동등성을 비교하며, 내부에서 클로저를 실행할 수 있는 기능을 제공합니다. | ||
public struct EquatableClosure: Equatable { | ||
/// EquatableClosure를 고유하게 식별할 수 있는 UUID입니다. | ||
private let id: UUID = UUID() | ||
/// 실행할 클로저 | ||
private let action: () -> Void | ||
|
||
public static func == (lhs: EquatableClosure, rhs: EquatableClosure) -> Bool { | ||
lhs.id == rhs.id | ||
} | ||
|
||
/// EquatableClosure 초기화 메서드 | ||
/// - Parameter action: 실행할 클로저 | ||
public init(action: @escaping () -> Void) { | ||
self.action = action | ||
} | ||
|
||
/// 클로저를 실행하는 메서드 | ||
public func execute() { | ||
action() | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
padding 한번만 확인해주세요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 부분 관련해서 확인한 결과, 현 스프린트 시점까지는 내부에 포함되는 컨텐츠 관련해서 -
alert 제외 동적으로 표시되는 화면이 없습니다. -> 해당 동적 컨텐츠 팝업 뷰의 고정 너비 or 외부 패딩 관련 정책이 없습니다.
현재 alert의 경우 내부 패딩을 적용한 상태로 고정 너비값을 가지므로 이를 메인으로 가져가면서,
추후 동적 컨텐츠이 포함된 디자인 확정 시 해당 정책 더블 체크해서 패딩값 바꿔놓겠습니다!