Skip to content
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

[MOB-945] 체크박스 컴포넌트 구현 #79

Open
wants to merge 14 commits into
base: feature/redesign_bezier
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions BezierSwift/Sources/Components/Checkbox/BezierCheckboxSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// BezierCheckboxSource.swift
//
//
// Created by 구본욱 on 8/16/24.
//

import SwiftUI

struct BezierCheckboxSource: View {
private let needStroke: Bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 사용처가 있을까요?

private let icon: BezierIcon?
private let strokeColor: BezierColor
private let backgroundColor: BezierColor
private let iconColor: BezierColor

init(
needStroke: Bool,
icon: BezierIcon?,
strokeColor: BezierColor,
backgroundColor: BezierColor,
iconColor: BezierColor
) {
self.needStroke = needStroke
self.icon = icon
self.strokeColor = strokeColor
self.backgroundColor = backgroundColor
self.iconColor = iconColor
}

var body: some View {
ZStack {
Circle()
.fill(self.backgroundColor.color)
.applyBezierBorder(
shape: Circle(),
style: self.strokeColor.color,
lineWidth: 2,
alignment: .inner
)

self.icon?.image
.frame(length: 16)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metric 추가하면 좋을 것 같아요!

.foregroundColor(self.iconColor.color)
}
.frame(length: 20)
.padding(.all, 2)
.compositingGroup()
}
}
21 changes: 21 additions & 0 deletions BezierSwift/Sources/Components/Checkbox/BezierCheckboxTypes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// BezierCheckboxTypes.swift
//
//
// Created by 구본욱 on 8/23/24.
//

import Foundation

// MARK: - BezierCheckboxColor
public enum BezierCheckboxColor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Color, Checked 는 BezierCheckbox 내부에서만 사용하고 있어서 안으로 들어가면 좋을 것 같아요!

case green
case blue
}

// MARK: - BezierCheckboxChecked
public enum BezierCheckboxChecked {
case checked
case unchecked
case indeterminate
}
217 changes: 217 additions & 0 deletions BezierSwift/Sources/Components/Checkbox/BezierPrimaryCheckbox.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
//
// BezierPrimaryCheckbox.swift
//
//
// Created by 구본욱 on 8/23/24.
//

import SwiftUI

// MARK: - Metric
private enum Metric {
static let minHeight: CGFloat = 40
static let labelTop: CGFloat = 11
}

/// 최소 1가지 이상의 옵션을 선택 또는 해제할 수 있는 컨트롤 컴포넌트로, 일반적으로 사용하는 체크박스 아이템입니다.
// MARK: - BezierPrimaryCheckbox
public struct BezierPrimaryCheckbox<Nested>: View where Nested: View {
public typealias Color = BezierCheckboxColor
public typealias Checked = BezierCheckboxChecked
public typealias NestedBuilder = () -> Nested

@Environment(\.isEnabled) var isEnabled

private let label: AttributedString?
private let color: Color
private let checked: Checked
private let showRequired: Bool
private let nestedBuilder: NestedBuilder

/// Nested가 존재하는 체크박스를 생성합니다.
/// - Parameters:
/// - label: 체크박스에 함께 표기될 텍스트를 지정합니다.
/// - color: 체크박스 소스가 표기될 색상을 지정합니다. blue와 green을 지정할 수 있습니다.
/// - checked: 체크박스의 체크 상태를 지정합니다. checked, unchecked, indeterminate를 지정할 수 있습니다.
/// - showRequired: * 표시(Asterisk)를 사용해서 필수 항목임을 표현할지 지정합니다.
/// - nestedBuilder: 체크박스의 하위 영역에 표시될 내용을 지정하는 뷰를 생성합니다.
public init(
label: String?,
color: Color,
checked: Checked,
showRequired: Bool,
@ViewBuilder nestedBuilder: @escaping NestedBuilder
) {
if let label {
self.label = AttributedString(label)
} else {
self.label = nil
}
self.color = color
self.checked = checked
self.showRequired = showRequired
self.nestedBuilder = nestedBuilder
}

/// Nested가 없는 체크박스를 생성합니다.
/// - Parameters:
/// - label: 체크박스에 함께 표기될 텍스트를 지정합니다.
/// - color: 체크박스 소스가 표기될 색상을 지정합니다. blue와 green을 지정할 수 있습니다.
/// - checked: 체크박스의 체크 상태를 지정합니다. checked, unchecked, indeterminate를 지정할 수 있습니다.
/// - showRequired: * 표시(Asterisk)를 사용해서 필수 항목임을 표현할지 지정합니다.
public init(
label: String?,
color: Color,
checked: Checked,
showRequired: Bool
) where Nested == EmptyView {
if let label {
self.label = AttributedString(label)
} else {
self.label = nil
}
self.color = color
self.checked = checked
self.showRequired = showRequired
self.nestedBuilder = { EmptyView() }
}

/// Nested가 존재하는 체크박스를 생성합니다.
/// - Parameters:
/// - label: 체크박스에 함께 표기될 AttributedString을 지정합니다.
/// - color: 체크박스 소스가 표기될 색상을 지정합니다. blue와 green을 지정할 수 있습니다.
/// - checked: 체크박스의 체크 상태를 지정합니다. checked, unchecked, indeterminate를 지정할 수 있습니다.
/// - showRequired: * 표시(Asterisk)를 사용해서 필수 항목임을 표현할지 지정합니다.
/// - nestedBuilder: 체크박스의 하위 영역에 표시될 내용을 지정하는 뷰를 생성합니다.
public init(
label: AttributedString?,
color: Color,
checked: Checked,
showRequired: Bool,
@ViewBuilder nestedBuilder: @escaping NestedBuilder
) {
self.label = label
self.color = color
self.checked = checked
self.showRequired = showRequired
self.nestedBuilder = nestedBuilder
}

/// Nested가 없는 체크박스를 생성합니다.
/// - Parameters:
/// - label: 체크박스에 함께 표기될 AttributedString을 지정합니다.
/// - color: 체크박스 소스가 표기될 색상을 지정합니다. blue와 green을 지정할 수 있습니다.
/// - checked: 체크박스의 체크 상태를 지정합니다. checked, unchecked, indeterminate를 지정할 수 있습니다.
/// - showRequired: * 표시(Asterisk)를 사용해서 필수 항목임을 표현할지 지정합니다.
public init(
label: AttributedString?,
color: Color,
checked: Checked,
showRequired: Bool
) where Nested == EmptyView {
self.label = label
self.color = color
self.checked = checked
self.showRequired = showRequired
self.nestedBuilder = { EmptyView() }
}

public var body: some View {
VStack(spacing: 0) {
HStack(alignment: .top, spacing: 0) {
ZStack(alignment: .center) {
BezierCheckboxSource(
needStroke: self.sourceNeedStroke,
icon: self.sourceIcon,
strokeColor: self.sourceStrokeColor,
backgroundColor: self.sourceBackgroundColor,
iconColor: self.sourceIconColor
)
}
.frame(length: Metric.minHeight)

HStack(alignment: .center, spacing: 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
HStack(alignment: .center, spacing: 0) {
HStack(alignment: .center, spacing: .zero) {

채널 엑스에서는 spacing 0이 들어가는 경우가 많아서 이때마다 Metric에 추가하기 어려워서 0 일 때만 .zero로 구분해주자 했었어요. 이 컨벤션을 베지어에서도 가져갈지 논의 해보면 좋을 것 같아요

Text(self.label ?? "")
.font(BezierFont.caption1Regular.font)
.lineLimit(nil)

if self.showRequired {
Text("*")
.font(BezierFont.caption1Regular.font)
.foregroundColor(BezierColor.fgRedNormal.color)
}

Spacer()
}
.padding(.vertical, Metric.labelTop)
}

VStack(alignment: .leading, spacing: 0) {
self.nestedBuilder()
}.padding(.leading, Metric.minHeight)
}
.frame(minHeight: Metric.minHeight)
.compositingGroup()
.applyDisabledStyle()
}
}

extension BezierPrimaryCheckbox {
private var sourceBackgroundColor: BezierColor {
switch self.checked {
case .checked, .indeterminate:
return self.color == .blue ? .primaryBgNormal : .bgGreenNormal
case .unchecked:
return self.isEnabled ? .bgWhiteNormal : .bgBlackDark
}
}

private var sourceNeedStroke: Bool {
return self.checked == .unchecked && self.isEnabled
}

private var sourceStrokeColor: BezierColor {
guard self.sourceNeedStroke else { return .bgWhiteAlphaTransparent }

return .bgBlackDark
}

private var sourceIcon: BezierIcon? {
switch self.checked {
case .checked:
return .checkBold
case .unchecked:
return nil
case .indeterminate:
return .hyphenBold
}
}

private var sourceIconColor: BezierColor {
return self.checked == .unchecked ? .bgWhiteAlphaTransparent : .fgAbsoluteWhiteDark
}
}

#Preview {
VStack {
BezierPrimaryCheckbox(
label: "Hello",
color: .blue,
checked: .indeterminate,
showRequired: true
) {
BezierSecondaryCheckbox(
label: "Secondary",
color: .blue,
checked: false
)
}
BezierPrimaryCheckbox(
label: "Hello",
color: .green,
checked: .indeterminate,
showRequired: true
)
}
.padding(.horizontal, 20)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// BezierSecondaryCheckbox.swift
//
//
// Created by 구본욱 on 8/23/24.
//

import SwiftUI

// MARK: - Metric
private enum Metric {
static let minHeight: CGFloat = 36
static let labelTop: CGFloat = 9
}

/// 최소 1가지 이상의 옵션을 선택 또는 해제할 수 있는 컨트롤 컴포넌트입니다. 위계상 primary 에 속할 때만 사용해야하며 단독으로 사용할 수 없고, Indent 되어 표시됩니다.
// MARK: - BezierSecondaryCheckbox
public struct BezierSecondaryCheckbox: View {
public typealias Color = BezierCheckboxColor

private let label: AttributedString?
private let color: Color
private let checked: Bool

/// BezierPrimaryCheckbox의 하위(Nested)에 추가될 수 있는 서브 체크박스를 생성합니다.
/// - Parameters:
/// - label: 체크박스에 함께 표기될 텍스트를 지정합니다.
/// - color: 체크박스 소스가 표기될 색상을 지정합니다. blue와 green을 지정할 수 있습니다.
/// - checked: 체크박스의 체크 상태를 지정합니다.
public init(label: String?, color: Color, checked: Bool) {
if let label {
self.label = AttributedString(label)
} else {
self.label = nil
}
self.color = color
self.checked = checked
}

/// BezierPrimaryCheckbox의 하위(Nested)에 추가될 수 있는 서브 체크박스를 생성합니다.
/// - Parameters:
/// - label: 체크박스에 함께 표기될 AttributedString를 지정합니다.
/// - color: 체크박스 소스가 표기될 색상을 지정합니다. blue와 green을 지정할 수 있습니다.
/// - checked: 체크박스의 체크 상태를 지정합니다.
public init(label: AttributedString?, color: Color, checked: Bool) {
self.label = label
self.color = color
self.checked = checked
}

public var body: some View {
HStack(alignment: .top, spacing: 0) {
ZStack(alignment: .center) {
BezierCheckboxSource(
needStroke: false,
icon: .checkBold,
strokeColor: .bgWhiteAlphaTransparent,
backgroundColor: .bgWhiteAlphaTransparent,
iconColor: self.sourceIconColor
)
}
.frame(length: Metric.minHeight)

Text(self.label ?? "")
.font(BezierFont.caption1Regular.font)
.lineLimit(nil)
.padding(.vertical, Metric.labelTop)

Spacer()
}
.frame(minHeight: Metric.minHeight)
.compositingGroup()
.applyDisabledStyle()
}
}

extension BezierSecondaryCheckbox {
private var sourceIconColor: BezierColor {
if self.checked {
return self.color == .blue ? .primaryBgNormal : .fgGreenNormal
} else {
return .fgBlackDark
}
}
}

#Preview {
BezierSecondaryCheckbox(
label: "hello",
color: .green,
checked: true
)
}
Loading