feat(component): 오디오 콘텐츠 카드를 추가한다
This commit is contained in:
154
SodaLive/Sources/V2/Component/AudioContentCard.swift
Normal file
154
SodaLive/Sources/V2/Component/AudioContentCard.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// AudioContentCard.swift
|
||||
// SodaLive
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum AudioContentCardSize {
|
||||
case large
|
||||
case medium
|
||||
case small
|
||||
|
||||
var width: CGFloat {
|
||||
switch self {
|
||||
case .large:
|
||||
return 185
|
||||
case .medium:
|
||||
return 163
|
||||
case .small:
|
||||
return 122
|
||||
}
|
||||
}
|
||||
|
||||
var labelWidth: CGFloat {
|
||||
width - (labelHorizontalPadding * 2)
|
||||
}
|
||||
|
||||
var labelHorizontalPadding: CGFloat {
|
||||
switch self {
|
||||
case .large:
|
||||
return 0
|
||||
case .medium:
|
||||
return SodaSpacing.s6
|
||||
case .small:
|
||||
return SodaSpacing.s4
|
||||
}
|
||||
}
|
||||
|
||||
var titleTypography: SodaTypography {
|
||||
switch self {
|
||||
case .large, .medium:
|
||||
return .heading4
|
||||
case .small:
|
||||
return .body1
|
||||
}
|
||||
}
|
||||
|
||||
var subtitleTypography: SodaTypography {
|
||||
switch self {
|
||||
case .large, .medium:
|
||||
return .body5
|
||||
case .small:
|
||||
return .caption2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AudioContentCard<Thumbnail: View>: View {
|
||||
let size: AudioContentCardSize
|
||||
let title: String
|
||||
let subtitle: String
|
||||
private let thumbnail: Thumbnail
|
||||
|
||||
init(
|
||||
size: AudioContentCardSize,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
@ViewBuilder thumbnail: () -> Thumbnail
|
||||
) {
|
||||
self.size = size
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.thumbnail = thumbnail()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: SodaSpacing.s8) {
|
||||
thumbnailContent
|
||||
|
||||
labelContent
|
||||
}
|
||||
.frame(width: size.width, alignment: .leading)
|
||||
}
|
||||
|
||||
private var thumbnailContent: some View {
|
||||
thumbnail
|
||||
.frame(width: size.width, height: size.width)
|
||||
.clipped()
|
||||
.frame(width: size.width, height: size.width)
|
||||
.clipShape(RoundedRectangle(cornerRadius: SodaSpacing.s14, style: .continuous))
|
||||
}
|
||||
|
||||
private var labelContent: some View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(title)
|
||||
.appFont(size.titleTypography)
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
|
||||
Text(subtitle)
|
||||
.appFont(size.subtitleTypography)
|
||||
.foregroundColor(Color.gray500)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
.frame(width: size.labelWidth, alignment: .leading)
|
||||
.padding(.horizontal, size.labelHorizontalPadding)
|
||||
.frame(width: size.width, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
struct AudioContentCard_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HStack(alignment: .top, spacing: SodaSpacing.s20) {
|
||||
AudioContentCard(
|
||||
size: .large,
|
||||
title: "오디오 콘텐츠 제목입니다",
|
||||
subtitle: "크리에이터명"
|
||||
) {
|
||||
previewThumbnail(Color.gray800)
|
||||
}
|
||||
|
||||
AudioContentCard(
|
||||
size: .medium,
|
||||
title: "긴 제목은 한 줄로 말줄임 처리됩니다",
|
||||
subtitle: "긴 부제목도 한 줄로 말줄임 처리됩니다"
|
||||
) {
|
||||
previewThumbnail(Color.gray700)
|
||||
}
|
||||
|
||||
AudioContentCard(
|
||||
size: .small,
|
||||
title: "스몰 카드",
|
||||
subtitle: "오디오"
|
||||
) {
|
||||
previewThumbnail(Color.gray900)
|
||||
}
|
||||
}
|
||||
.padding(SodaSpacing.s20)
|
||||
.background(Color.black)
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
|
||||
private static func previewThumbnail(_ color: Color) -> some View {
|
||||
ZStack {
|
||||
color
|
||||
|
||||
Image(systemName: "waveform")
|
||||
.font(.system(size: 32, weight: .bold))
|
||||
.foregroundColor(Color.gray500)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user