CHResume/ResumeBuilder/Main Elements/TopView.swift

107 lines
3.8 KiB
Swift
Raw Normal View History

2023-08-18 22:47:24 +02:00
import SwiftUI
import SFSafeSymbols
2023-11-10 11:22:49 +01:00
import PhoneNumberKit
private let emailPattern = #"^\S+@\S+\.\S+$"#
2023-08-18 22:47:24 +02:00
struct TopView: View {
let info: TopInfo
let style: HeaderStyle
2023-08-23 16:17:34 +02:00
let accent: Color
2023-11-10 11:22:49 +01:00
private let phoneNumberKit = PhoneNumberKit()
var isValidEmail: Bool {
info.email.range(of: emailPattern, options: .regularExpression) != nil
}
var isValidPhoneNumber: Bool {
do {
_ = try phoneNumberKit.parse(info.phone)
return true
} catch {
return false
}
}
2023-08-23 16:17:34 +02:00
2023-08-18 22:47:24 +02:00
var body: some View {
GeometryReader { geo in
let sideWidth = max(0, (geo.size.width - geo.size.height) / 2)
HStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0) {
Text(info.name)
.font(.title)
2023-08-23 16:17:34 +02:00
.foregroundColor(accent)
2023-08-18 22:47:24 +02:00
Spacer(minLength: 0)
Text(info.tagLine)
.font(.subheadline)
.padding(.trailing, style.imageShadowSize)
Spacer(minLength: 0)
HStack {
RightImageLabel(info.place, systemSymbol: .house)
RightImageLabel(info.ageText, systemSymbol: .hourglass)
2023-08-20 13:11:13 +02:00
Spacer()
2023-08-18 22:47:24 +02:00
}.font(.subheadline)
}
.frame(width: sideWidth)
TopViewImage(
image: info.imageName,
shadow: style.imageShadowSize,
lineWidth: style.imageBorderWidth)
VStack(alignment: .trailing) {
2023-11-10 11:22:49 +01:00
LeftImageLabel(systemSymbol: .globe) {
Link(info.web, destination: URL(string: "https://" + info.web)!)
}
.frame(maxHeight: style.iconHeight)
2023-08-18 22:47:24 +02:00
Spacer()
2023-11-10 11:22:49 +01:00
LeftImageLabel(systemSymbol: .envelope) {
Link(info.email, destination: URL(string: "mailto:" + info.email)!)
.disabled(!isValidEmail)
}
.frame(maxHeight: style.iconHeight)
2023-08-18 22:47:24 +02:00
Spacer()
2023-11-10 11:22:49 +01:00
LeftImageLabel(systemSymbol: .phone) {
Link(info.phone, destination: URL(string: "tel:" + info.phone)!)
.disabled(!isValidPhoneNumber)
}
.frame(maxHeight: style.iconHeight)
2023-08-18 22:47:24 +02:00
Spacer()
HStack(spacing: 0) {
Spacer()
2023-11-10 11:22:49 +01:00
Link(info.github, destination: URL(string: "https://" + info.github)!)
2023-08-18 22:47:24 +02:00
Image("Github")
.resizable()
.aspectRatio(1.0, contentMode: .fit)
.padding(2)
.frame(width: style.iconHeight)
}.frame(maxHeight: style.iconHeight)
}
.font(.subheadline)
.frame(width: sideWidth)
2023-11-10 11:22:49 +01:00
.foregroundStyle(.primary)
2023-08-18 22:47:24 +02:00
}
}
}
}
struct TopView_Previews: PreviewProvider {
static var previews: some View {
TopView(info: .init(
imageName: "Cover",
name: "Christoph Hagen",
2023-08-20 13:11:13 +02:00
tagLine: "Problem solver with a favour for interdisciplinary work.",
2023-08-18 22:47:24 +02:00
place: "Würzburg, Germany",
ageText: "Age 32",
web: "christophhagen.de",
email: "jobs@christophhagen.de",
phone: "Upon Request",
github: "github.com/christophhagen"),
2023-08-23 16:17:34 +02:00
style: HeaderStyle(),
accent: .orange)
2023-08-18 22:47:24 +02:00
.previewLayout(.fixed(width: 540, height: 120))
}
}