|
@@ -62,21 +62,8 @@ final class CVTemplatePreviewView: NSView {
|
|
|
private let palette: CVTemplateCardPalette
|
|
private let palette: CVTemplateCardPalette
|
|
|
private let paper = NSView()
|
|
private let paper = NSView()
|
|
|
|
|
|
|
|
- /// Stable 0…11 from template identity so each row looks different (not just by `family`).
|
|
|
|
|
- private var idVariant: Int {
|
|
|
|
|
- var h: UInt64 = 1469598103934665603
|
|
|
|
|
- let layoutDesc: String
|
|
|
|
|
- switch template.layout {
|
|
|
|
|
- case .singleColumn: layoutDesc = "1col"
|
|
|
|
|
- case .twoColumn(let s, let t): layoutDesc = "2col_\(s)_\(t)"
|
|
|
|
|
- }
|
|
|
|
|
- let blob = "\(template.id)|\(template.family.rawValue)|\(template.headline)|\(template.accent)|\(layoutDesc)|\(template.sectionLabelStyle)"
|
|
|
|
|
- for b in blob.utf8 {
|
|
|
|
|
- h ^= UInt64(b)
|
|
|
|
|
- h &*= 1_099_511_628_211
|
|
|
|
|
- }
|
|
|
|
|
- return Int(h % 12)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /// Same variant index as `CVProfileDocumentView` (shared `CVTemplate.galleryLayoutVariant`).
|
|
|
|
|
+ private var layoutVariant: Int { template.galleryLayoutVariant }
|
|
|
|
|
|
|
|
init(template: CVTemplate, palette: CVTemplateCardPalette) {
|
|
init(template: CVTemplate, palette: CVTemplateCardPalette) {
|
|
|
self.template = template
|
|
self.template = template
|
|
@@ -133,29 +120,23 @@ final class CVTemplatePreviewView: NSView {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func paperBackgroundColor() -> NSColor {
|
|
private func paperBackgroundColor() -> NSColor {
|
|
|
- switch idVariant % 5 {
|
|
|
|
|
- case 0: return palette.previewPaper
|
|
|
|
|
- case 1: return NSColor(srgbRed: 0.995, green: 0.992, blue: 0.985, alpha: 1)
|
|
|
|
|
- case 2: return NSColor(srgbRed: 0.96, green: 0.99, blue: 1, alpha: 1)
|
|
|
|
|
- case 3: return NSColor(srgbRed: 0.99, green: 0.99, blue: 0.99, alpha: 1)
|
|
|
|
|
- default: return NSColor(srgbRed: 0.99, green: 0.98, blue: 0.995, alpha: 1)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ CVResumeAppearance.paperBackground(variant: layoutVariant, base: palette.previewPaper)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Family: Professional (ATS-friendly)
|
|
// MARK: - Family: Professional (ATS-friendly)
|
|
|
|
|
|
|
|
private func buildProfessionalResume() -> NSView {
|
|
private func buildProfessionalResume() -> NSView {
|
|
|
- let swapExpFirst = (idVariant % 3) == 1
|
|
|
|
|
- let sidebarMult: CGFloat = (idVariant % 5 == 2) ? 0.38 : 0.34
|
|
|
|
|
|
|
+ let swapExpFirst = (layoutVariant % 3) == 1
|
|
|
|
|
+ let sidebarMult: CGFloat = (layoutVariant % 5 == 2) ? 0.38 : 0.34
|
|
|
|
|
|
|
|
switch template.layout {
|
|
switch template.layout {
|
|
|
case .singleColumn:
|
|
case .singleColumn:
|
|
|
let v = NSStackView()
|
|
let v = NSStackView()
|
|
|
v.orientation = .vertical
|
|
v.orientation = .vertical
|
|
|
- v.spacing = 4 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ v.spacing = 4 + CGFloat(layoutVariant % 3)
|
|
|
v.alignment = .leading
|
|
v.alignment = .leading
|
|
|
v.addArrangedSubview(proHeaderBlock())
|
|
v.addArrangedSubview(proHeaderBlock())
|
|
|
- if (idVariant % 6) == 4 {
|
|
|
|
|
|
|
+ if (layoutVariant % 6) == 4 {
|
|
|
v.addArrangedSubview(proInlineSkillsRow())
|
|
v.addArrangedSubview(proInlineSkillsRow())
|
|
|
}
|
|
}
|
|
|
v.addArrangedSubview(hairline())
|
|
v.addArrangedSubview(hairline())
|
|
@@ -169,9 +150,9 @@ final class CVTemplatePreviewView: NSView {
|
|
|
let rule = hairline()
|
|
let rule = hairline()
|
|
|
let row = NSStackView()
|
|
let row = NSStackView()
|
|
|
row.orientation = .horizontal
|
|
row.orientation = .horizontal
|
|
|
- row.spacing = 5 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ row.spacing = 5 + CGFloat(layoutVariant % 3)
|
|
|
row.alignment = .top
|
|
row.alignment = .top
|
|
|
- let sidebar = proSidebarColumn(tinted: tinted, variant: idVariant)
|
|
|
|
|
|
|
+ let sidebar = proSidebarColumn(tinted: tinted, variant: layoutVariant)
|
|
|
let main = proMainColumn(compact: true, experienceFirst: swapExpFirst)
|
|
let main = proMainColumn(compact: true, experienceFirst: swapExpFirst)
|
|
|
if side == .leading {
|
|
if side == .leading {
|
|
|
row.addArrangedSubview(sidebar)
|
|
row.addArrangedSubview(sidebar)
|
|
@@ -315,7 +296,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
private func proMainColumn(compact: Bool, experienceFirst: Bool) -> NSView {
|
|
private func proMainColumn(compact: Bool, experienceFirst: Bool) -> NSView {
|
|
|
let stack = NSStackView()
|
|
let stack = NSStackView()
|
|
|
stack.orientation = .vertical
|
|
stack.orientation = .vertical
|
|
|
- stack.spacing = compact ? 4 : 5 + CGFloat(idVariant % 2)
|
|
|
|
|
|
|
+ stack.spacing = compact ? 4 : 5 + CGFloat(layoutVariant % 2)
|
|
|
stack.alignment = .leading
|
|
stack.alignment = .leading
|
|
|
let sp: CGFloat = compact ? 6.2 : 6.5
|
|
let sp: CGFloat = compact ? 6.2 : 6.5
|
|
|
|
|
|
|
@@ -348,7 +329,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
// MARK: - Family: Modern (three distinct silhouettes per id)
|
|
// MARK: - Family: Modern (three distinct silhouettes per id)
|
|
|
|
|
|
|
|
private func buildModernResume() -> NSView {
|
|
private func buildModernResume() -> NSView {
|
|
|
- switch idVariant % 3 {
|
|
|
|
|
|
|
+ switch layoutVariant % 3 {
|
|
|
case 0: return buildModernClassicBandLayout()
|
|
case 0: return buildModernClassicBandLayout()
|
|
|
case 1: return buildModernRailDocLayout()
|
|
case 1: return buildModernRailDocLayout()
|
|
|
default: return buildModernSplitHeaderLayout()
|
|
default: return buildModernSplitHeaderLayout()
|
|
@@ -364,7 +345,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
let sideCol = modernSidebar(theme: theme, tinted: tinted)
|
|
let sideCol = modernSidebar(theme: theme, tinted: tinted)
|
|
|
let row = NSStackView()
|
|
let row = NSStackView()
|
|
|
row.orientation = .horizontal
|
|
row.orientation = .horizontal
|
|
|
- row.spacing = 5 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ row.spacing = 5 + CGFloat(layoutVariant % 3)
|
|
|
row.alignment = .top
|
|
row.alignment = .top
|
|
|
if side == .leading {
|
|
if side == .leading {
|
|
|
row.addArrangedSubview(sideCol)
|
|
row.addArrangedSubview(sideCol)
|
|
@@ -373,7 +354,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
row.addArrangedSubview(main)
|
|
row.addArrangedSubview(main)
|
|
|
row.addArrangedSubview(sideCol)
|
|
row.addArrangedSubview(sideCol)
|
|
|
}
|
|
}
|
|
|
- let mult: CGFloat = (idVariant % 4 == 2) ? 0.36 : 0.32
|
|
|
|
|
|
|
+ let mult: CGFloat = (layoutVariant % 4 == 2) ? 0.36 : 0.32
|
|
|
sideCol.widthAnchor.constraint(equalTo: row.widthAnchor, multiplier: mult).isActive = true
|
|
sideCol.widthAnchor.constraint(equalTo: row.widthAnchor, multiplier: mult).isActive = true
|
|
|
return row
|
|
return row
|
|
|
}
|
|
}
|
|
@@ -385,7 +366,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
header.translatesAutoresizingMaskIntoConstraints = false
|
|
header.translatesAutoresizingMaskIntoConstraints = false
|
|
|
header.wantsLayer = true
|
|
header.wantsLayer = true
|
|
|
header.layer?.backgroundColor = theme.cgColor
|
|
header.layer?.backgroundColor = theme.cgColor
|
|
|
- header.layer?.cornerRadius = idVariant % 2 == 0 ? 5 : 3
|
|
|
|
|
|
|
+ header.layer?.cornerRadius = layoutVariant % 2 == 0 ? 5 : 3
|
|
|
|
|
|
|
|
let white = NSColor.white
|
|
let white = NSColor.white
|
|
|
let name = makeLabel(CVPreviewDemoContent.fullName, font: .systemFont(ofSize: 9, weight: .bold), color: white, alignment: .left, maxLines: 1)
|
|
let name = makeLabel(CVPreviewDemoContent.fullName, font: .systemFont(ofSize: 9, weight: .bold), color: white, alignment: .left, maxLines: 1)
|
|
@@ -423,13 +404,13 @@ final class CVTemplatePreviewView: NSView {
|
|
|
topRow.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -7),
|
|
topRow.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -7),
|
|
|
topRow.topAnchor.constraint(equalTo: header.topAnchor, constant: 6),
|
|
topRow.topAnchor.constraint(equalTo: header.topAnchor, constant: 6),
|
|
|
topRow.bottomAnchor.constraint(equalTo: header.bottomAnchor, constant: -6),
|
|
topRow.bottomAnchor.constraint(equalTo: header.bottomAnchor, constant: -6),
|
|
|
- header.heightAnchor.constraint(greaterThanOrEqualToConstant: 32 + CGFloat(idVariant % 3) * 2)
|
|
|
|
|
|
|
+ header.heightAnchor.constraint(greaterThanOrEqualToConstant: 32 + CGFloat(layoutVariant % 3) * 2)
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
let body = modernPrimaryBody(theme: theme)
|
|
let body = modernPrimaryBody(theme: theme)
|
|
|
let wrap = NSStackView(views: [header, body])
|
|
let wrap = NSStackView(views: [header, body])
|
|
|
wrap.orientation = .vertical
|
|
wrap.orientation = .vertical
|
|
|
- wrap.spacing = 6 + CGFloat(idVariant % 2)
|
|
|
|
|
|
|
+ wrap.spacing = 6 + CGFloat(layoutVariant % 2)
|
|
|
wrap.alignment = .leading
|
|
wrap.alignment = .leading
|
|
|
return wrap
|
|
return wrap
|
|
|
}
|
|
}
|
|
@@ -443,7 +424,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
rail.wantsLayer = true
|
|
rail.wantsLayer = true
|
|
|
rail.layer?.backgroundColor = theme.cgColor
|
|
rail.layer?.backgroundColor = theme.cgColor
|
|
|
rail.layer?.cornerRadius = 2
|
|
rail.layer?.cornerRadius = 2
|
|
|
- rail.widthAnchor.constraint(equalToConstant: 3 + CGFloat(idVariant % 2)).isActive = true
|
|
|
|
|
|
|
+ rail.widthAnchor.constraint(equalToConstant: 3 + CGFloat(layoutVariant % 2)).isActive = true
|
|
|
|
|
|
|
|
let inner = NSStackView()
|
|
let inner = NSStackView()
|
|
|
inner.orientation = .vertical
|
|
inner.orientation = .vertical
|
|
@@ -578,18 +559,18 @@ final class CVTemplatePreviewView: NSView {
|
|
|
private func buildMinimalResume() -> NSView {
|
|
private func buildMinimalResume() -> NSView {
|
|
|
let ink = palette.previewInk
|
|
let ink = palette.previewInk
|
|
|
let muted = palette.previewMuted
|
|
let muted = palette.previewMuted
|
|
|
- let nameWeight: NSFont.Weight = (idVariant % 3 == 0) ? .ultraLight : .light
|
|
|
|
|
- let nameSize: CGFloat = template.headline == .centered ? 11 + CGFloat(idVariant % 2) : 9.5 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ let nameWeight: NSFont.Weight = (layoutVariant % 3 == 0) ? .ultraLight : .light
|
|
|
|
|
+ let nameSize: CGFloat = template.headline == .centered ? 11 + CGFloat(layoutVariant % 2) : 9.5 + CGFloat(layoutVariant % 3)
|
|
|
let name = makeLabel(CVPreviewDemoContent.fullName, font: .systemFont(ofSize: nameSize, weight: nameWeight), color: ink, alignment: .left, maxLines: 1)
|
|
let name = makeLabel(CVPreviewDemoContent.fullName, font: .systemFont(ofSize: nameSize, weight: nameWeight), color: ink, alignment: .left, maxLines: 1)
|
|
|
let role = makeLabel(CVPreviewDemoContent.title.uppercased(), font: .systemFont(ofSize: 6.5, weight: .medium), color: muted, alignment: .left, maxLines: 1)
|
|
let role = makeLabel(CVPreviewDemoContent.title.uppercased(), font: .systemFont(ofSize: 6.5, weight: .medium), color: muted, alignment: .left, maxLines: 1)
|
|
|
let contact = makeLabel("\(CVPreviewDemoContent.email) \(CVPreviewDemoContent.phone)", font: .systemFont(ofSize: 5.8, weight: .regular), color: muted.withAlphaComponent(0.75), alignment: .left, maxLines: 1)
|
|
let contact = makeLabel("\(CVPreviewDemoContent.email) \(CVPreviewDemoContent.phone)", font: .systemFont(ofSize: 5.8, weight: .regular), color: muted.withAlphaComponent(0.75), alignment: .left, maxLines: 1)
|
|
|
|
|
|
|
|
let head = NSStackView()
|
|
let head = NSStackView()
|
|
|
head.orientation = .vertical
|
|
head.orientation = .vertical
|
|
|
- head.spacing = template.headline == .avatarStacked ? 8 : 4 + CGFloat(idVariant % 4)
|
|
|
|
|
|
|
+ head.spacing = template.headline == .avatarStacked ? 8 : 4 + CGFloat(layoutVariant % 4)
|
|
|
head.alignment = template.headline == .centered ? .centerX : .leading
|
|
head.alignment = template.headline == .centered ? .centerX : .leading
|
|
|
if template.headline == .avatarStacked {
|
|
if template.headline == .avatarStacked {
|
|
|
- head.addArrangedSubview(initialsAvatar(diameter: 24 + CGFloat(idVariant % 2) * 2, ink: ink))
|
|
|
|
|
|
|
+ head.addArrangedSubview(initialsAvatar(diameter: 24 + CGFloat(layoutVariant % 2) * 2, ink: ink))
|
|
|
}
|
|
}
|
|
|
if template.headline == .centered {
|
|
if template.headline == .centered {
|
|
|
name.alignment = .center
|
|
name.alignment = .center
|
|
@@ -600,21 +581,21 @@ final class CVTemplatePreviewView: NSView {
|
|
|
head.addArrangedSubview(role)
|
|
head.addArrangedSubview(role)
|
|
|
head.addArrangedSubview(contact)
|
|
head.addArrangedSubview(contact)
|
|
|
head.addArrangedSubview(hairlineSoft())
|
|
head.addArrangedSubview(hairlineSoft())
|
|
|
- if idVariant % 5 == 1 {
|
|
|
|
|
|
|
+ if layoutVariant % 5 == 1 {
|
|
|
head.addArrangedSubview(hairlineSoft())
|
|
head.addArrangedSubview(hairlineSoft())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let swapEdu = (idVariant % 4) == 2
|
|
|
|
|
|
|
+ let swapEdu = (layoutVariant % 4) == 2
|
|
|
let body: NSView
|
|
let body: NSView
|
|
|
switch template.layout {
|
|
switch template.layout {
|
|
|
case .singleColumn:
|
|
case .singleColumn:
|
|
|
- body = minimalBody(spacing: 6 + CGFloat(idVariant % 3), educationBeforeExperience: swapEdu)
|
|
|
|
|
|
|
+ body = minimalBody(spacing: 6 + CGFloat(layoutVariant % 3), educationBeforeExperience: swapEdu)
|
|
|
case .twoColumn(let side, _):
|
|
case .twoColumn(let side, _):
|
|
|
let a = minimalBody(spacing: 5, educationBeforeExperience: swapEdu)
|
|
let a = minimalBody(spacing: 5, educationBeforeExperience: swapEdu)
|
|
|
- let b = minimalAside(numbered: idVariant % 3 == 1)
|
|
|
|
|
|
|
+ let b = minimalAside(numbered: layoutVariant % 3 == 1)
|
|
|
let row = NSStackView()
|
|
let row = NSStackView()
|
|
|
row.orientation = .horizontal
|
|
row.orientation = .horizontal
|
|
|
- row.spacing = 8 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ row.spacing = 8 + CGFloat(layoutVariant % 3)
|
|
|
row.alignment = .top
|
|
row.alignment = .top
|
|
|
if side == .leading {
|
|
if side == .leading {
|
|
|
row.addArrangedSubview(b)
|
|
row.addArrangedSubview(b)
|
|
@@ -623,14 +604,14 @@ final class CVTemplatePreviewView: NSView {
|
|
|
row.addArrangedSubview(a)
|
|
row.addArrangedSubview(a)
|
|
|
row.addArrangedSubview(b)
|
|
row.addArrangedSubview(b)
|
|
|
}
|
|
}
|
|
|
- let mult: CGFloat = (idVariant % 5 == 0) ? 0.34 : 0.3
|
|
|
|
|
|
|
+ let mult: CGFloat = (layoutVariant % 5 == 0) ? 0.34 : 0.3
|
|
|
b.widthAnchor.constraint(equalTo: row.widthAnchor, multiplier: mult).isActive = true
|
|
b.widthAnchor.constraint(equalTo: row.widthAnchor, multiplier: mult).isActive = true
|
|
|
body = row
|
|
body = row
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let wrap = NSStackView(views: [head, body])
|
|
let wrap = NSStackView(views: [head, body])
|
|
|
wrap.orientation = .vertical
|
|
wrap.orientation = .vertical
|
|
|
- wrap.spacing = 7 + CGFloat(idVariant % 2)
|
|
|
|
|
|
|
+ wrap.spacing = 7 + CGFloat(layoutVariant % 2)
|
|
|
wrap.alignment = .leading
|
|
wrap.alignment = .leading
|
|
|
return wrap
|
|
return wrap
|
|
|
}
|
|
}
|
|
@@ -690,13 +671,13 @@ final class CVTemplatePreviewView: NSView {
|
|
|
let ink = palette.previewInk
|
|
let ink = palette.previewInk
|
|
|
let muted = palette.previewMuted
|
|
let muted = palette.previewMuted
|
|
|
let theme = template.themeColor
|
|
let theme = template.themeColor
|
|
|
- let centeredHead = (idVariant % 2) == 0
|
|
|
|
|
|
|
+ let centeredHead = (layoutVariant % 2) == 0
|
|
|
|
|
|
|
|
let name = makeLabel(CVPreviewDemoContent.fullName, font: serifBold, color: ink, alignment: centeredHead ? .center : .left, maxLines: 1)
|
|
let name = makeLabel(CVPreviewDemoContent.fullName, font: serifBold, color: ink, alignment: centeredHead ? .center : .left, maxLines: 1)
|
|
|
let role = makeLabel(CVPreviewDemoContent.title, font: serif, color: muted, alignment: centeredHead ? .center : .left, maxLines: 1)
|
|
let role = makeLabel(CVPreviewDemoContent.title, font: serif, color: muted, alignment: centeredHead ? .center : .left, maxLines: 1)
|
|
|
let contact = makeLabel("\(CVPreviewDemoContent.email) · \(CVPreviewDemoContent.phone) · \(CVPreviewDemoContent.location)", font: NSFont(name: "Georgia", size: 5.8) ?? serif, color: muted.withAlphaComponent(0.9), alignment: centeredHead ? .center : .left, maxLines: 2)
|
|
let contact = makeLabel("\(CVPreviewDemoContent.email) · \(CVPreviewDemoContent.phone) · \(CVPreviewDemoContent.location)", font: NSFont(name: "Georgia", size: 5.8) ?? serif, color: muted.withAlphaComponent(0.9), alignment: centeredHead ? .center : .left, maxLines: 2)
|
|
|
|
|
|
|
|
- let rule = executiveRule(theme: theme, wide: idVariant % 3 == 0)
|
|
|
|
|
|
|
+ let rule = executiveRule(theme: theme, wide: layoutVariant % 3 == 0)
|
|
|
let head = NSStackView(views: [name, role, contact, rule])
|
|
let head = NSStackView(views: [name, role, contact, rule])
|
|
|
head.orientation = .vertical
|
|
head.orientation = .vertical
|
|
|
head.spacing = 4
|
|
head.spacing = 4
|
|
@@ -705,13 +686,13 @@ final class CVTemplatePreviewView: NSView {
|
|
|
let body: NSView
|
|
let body: NSView
|
|
|
switch template.layout {
|
|
switch template.layout {
|
|
|
case .singleColumn:
|
|
case .singleColumn:
|
|
|
- body = executiveBody(serif: serif, ink: ink, muted: muted, theme: theme, compact: false, tightLeading: idVariant % 5 == 2)
|
|
|
|
|
|
|
+ body = executiveBody(serif: serif, ink: ink, muted: muted, theme: theme, compact: false, tightLeading: layoutVariant % 5 == 2)
|
|
|
case .twoColumn(let side, let tinted):
|
|
case .twoColumn(let side, let tinted):
|
|
|
- let main = executiveBody(serif: serif, ink: ink, muted: muted, theme: theme, compact: true, tightLeading: idVariant % 5 == 2)
|
|
|
|
|
- let sideC = executiveSidebar(serif: serif, tinted: tinted, showMetrics: idVariant % 4 == 1)
|
|
|
|
|
|
|
+ let main = executiveBody(serif: serif, ink: ink, muted: muted, theme: theme, compact: true, tightLeading: layoutVariant % 5 == 2)
|
|
|
|
|
+ let sideC = executiveSidebar(serif: serif, tinted: tinted, showMetrics: layoutVariant % 4 == 1)
|
|
|
let row = NSStackView()
|
|
let row = NSStackView()
|
|
|
row.orientation = .horizontal
|
|
row.orientation = .horizontal
|
|
|
- row.spacing = 6 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ row.spacing = 6 + CGFloat(layoutVariant % 3)
|
|
|
row.alignment = .top
|
|
row.alignment = .top
|
|
|
if side == .leading {
|
|
if side == .leading {
|
|
|
row.addArrangedSubview(sideC)
|
|
row.addArrangedSubview(sideC)
|
|
@@ -720,14 +701,14 @@ final class CVTemplatePreviewView: NSView {
|
|
|
row.addArrangedSubview(main)
|
|
row.addArrangedSubview(main)
|
|
|
row.addArrangedSubview(sideC)
|
|
row.addArrangedSubview(sideC)
|
|
|
}
|
|
}
|
|
|
- let mult: CGFloat = (idVariant % 5 == 3) ? 0.38 : 0.33
|
|
|
|
|
|
|
+ let mult: CGFloat = (layoutVariant % 5 == 3) ? 0.38 : 0.33
|
|
|
sideC.widthAnchor.constraint(equalTo: row.widthAnchor, multiplier: mult).isActive = true
|
|
sideC.widthAnchor.constraint(equalTo: row.widthAnchor, multiplier: mult).isActive = true
|
|
|
body = row
|
|
body = row
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let wrap = NSStackView(views: [head, body])
|
|
let wrap = NSStackView(views: [head, body])
|
|
|
wrap.orientation = .vertical
|
|
wrap.orientation = .vertical
|
|
|
- wrap.spacing = 6 + CGFloat(idVariant % 2)
|
|
|
|
|
|
|
+ wrap.spacing = 6 + CGFloat(layoutVariant % 2)
|
|
|
wrap.alignment = .leading
|
|
wrap.alignment = .leading
|
|
|
return wrap
|
|
return wrap
|
|
|
}
|
|
}
|
|
@@ -747,7 +728,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
stack.orientation = .vertical
|
|
stack.orientation = .vertical
|
|
|
stack.spacing = (compact ? 4 : 5) - (tightLeading ? 1 : 0)
|
|
stack.spacing = (compact ? 4 : 5) - (tightLeading ? 1 : 0)
|
|
|
stack.alignment = .leading
|
|
stack.alignment = .leading
|
|
|
- let sumTitle = (idVariant % 6 == 3) ? "SUMMARY" : "PROFESSIONAL SUMMARY"
|
|
|
|
|
|
|
+ let sumTitle = (layoutVariant % 6 == 3) ? "SUMMARY" : "PROFESSIONAL SUMMARY"
|
|
|
stack.addArrangedSubview(sectionHeading(sumTitle))
|
|
stack.addArrangedSubview(sectionHeading(sumTitle))
|
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: serif, color: ink, alignment: .left, maxLines: 3))
|
|
stack.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: serif, color: ink, alignment: .left, maxLines: 3))
|
|
|
stack.addArrangedSubview(sectionHeading("SELECTED EXPERIENCE"))
|
|
stack.addArrangedSubview(sectionHeading("SELECTED EXPERIENCE"))
|
|
@@ -768,11 +749,11 @@ final class CVTemplatePreviewView: NSView {
|
|
|
stack.alignment = .leading
|
|
stack.alignment = .leading
|
|
|
if tinted {
|
|
if tinted {
|
|
|
stack.wantsLayer = true
|
|
stack.wantsLayer = true
|
|
|
- let fill = (idVariant % 3 == 0)
|
|
|
|
|
|
|
+ let fill = (layoutVariant % 3 == 0)
|
|
|
? NSColor(srgbRed: 0.97, green: 0.97, blue: 0.98, alpha: 1)
|
|
? NSColor(srgbRed: 0.97, green: 0.97, blue: 0.98, alpha: 1)
|
|
|
: template.themeColor.withAlphaComponent(0.07)
|
|
: template.themeColor.withAlphaComponent(0.07)
|
|
|
stack.layer?.backgroundColor = fill.cgColor
|
|
stack.layer?.backgroundColor = fill.cgColor
|
|
|
- stack.layer?.cornerRadius = idVariant % 4 == 2 ? 5 : 3
|
|
|
|
|
|
|
+ stack.layer?.cornerRadius = layoutVariant % 4 == 2 ? 5 : 3
|
|
|
stack.edgeInsets = NSEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
|
|
stack.edgeInsets = NSEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
|
|
|
}
|
|
}
|
|
|
stack.addArrangedSubview(sectionHeading("CORE COMPETENCIES"))
|
|
stack.addArrangedSubview(sectionHeading("CORE COMPETENCIES"))
|
|
@@ -794,18 +775,18 @@ final class CVTemplatePreviewView: NSView {
|
|
|
let theme = template.themeColor
|
|
let theme = template.themeColor
|
|
|
let deep = creativeDeepBackground(theme: theme)
|
|
let deep = creativeDeepBackground(theme: theme)
|
|
|
let onSidebar = NSColor.white.withAlphaComponent(0.95)
|
|
let onSidebar = NSColor.white.withAlphaComponent(0.95)
|
|
|
- let skillPrefix = (idVariant % 3 == 0) ? "• " : "▸ "
|
|
|
|
|
|
|
+ let skillPrefix = (layoutVariant % 3 == 0) ? "• " : "▸ "
|
|
|
|
|
|
|
|
let sidebar = NSStackView()
|
|
let sidebar = NSStackView()
|
|
|
sidebar.orientation = .vertical
|
|
sidebar.orientation = .vertical
|
|
|
- sidebar.spacing = 4 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ sidebar.spacing = 4 + CGFloat(layoutVariant % 3)
|
|
|
sidebar.alignment = .leading
|
|
sidebar.alignment = .leading
|
|
|
sidebar.wantsLayer = true
|
|
sidebar.wantsLayer = true
|
|
|
sidebar.layer?.backgroundColor = deep.cgColor
|
|
sidebar.layer?.backgroundColor = deep.cgColor
|
|
|
- sidebar.layer?.cornerRadius = idVariant % 2 == 0 ? 6 : 4
|
|
|
|
|
|
|
+ sidebar.layer?.cornerRadius = layoutVariant % 2 == 0 ? 6 : 4
|
|
|
sidebar.edgeInsets = NSEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
|
|
sidebar.edgeInsets = NSEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
|
|
|
|
|
|
|
|
- let sbTitle = makeLabel(CVPreviewDemoContent.fullName, font: .systemFont(ofSize: 7.5 + CGFloat(idVariant % 2), weight: .bold), color: onSidebar, alignment: .left, maxLines: 2)
|
|
|
|
|
|
|
+ let sbTitle = makeLabel(CVPreviewDemoContent.fullName, font: .systemFont(ofSize: 7.5 + CGFloat(layoutVariant % 2), weight: .bold), color: onSidebar, alignment: .left, maxLines: 2)
|
|
|
sidebar.addArrangedSubview(sbTitle)
|
|
sidebar.addArrangedSubview(sbTitle)
|
|
|
sidebar.addArrangedSubview(makeLabel(CVPreviewDemoContent.title, font: .systemFont(ofSize: 6.5, weight: .medium), color: onSidebar.withAlphaComponent(0.85), alignment: .left, maxLines: 2))
|
|
sidebar.addArrangedSubview(makeLabel(CVPreviewDemoContent.title, font: .systemFont(ofSize: 6.5, weight: .medium), color: onSidebar.withAlphaComponent(0.85), alignment: .left, maxLines: 2))
|
|
|
sidebar.addArrangedSubview(makeLabel(CVPreviewDemoContent.email, font: .systemFont(ofSize: 5.8), color: onSidebar.withAlphaComponent(0.8), alignment: .left, maxLines: 1))
|
|
sidebar.addArrangedSubview(makeLabel(CVPreviewDemoContent.email, font: .systemFont(ofSize: 5.8), color: onSidebar.withAlphaComponent(0.8), alignment: .left, maxLines: 1))
|
|
@@ -817,20 +798,20 @@ final class CVTemplatePreviewView: NSView {
|
|
|
|
|
|
|
|
let main = NSStackView()
|
|
let main = NSStackView()
|
|
|
main.orientation = .vertical
|
|
main.orientation = .vertical
|
|
|
- main.spacing = 4 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ main.spacing = 4 + CGFloat(layoutVariant % 3)
|
|
|
main.alignment = .leading
|
|
main.alignment = .leading
|
|
|
main.addArrangedSubview(creativeMainHeader(theme: theme))
|
|
main.addArrangedSubview(creativeMainHeader(theme: theme))
|
|
|
main.addArrangedSubview(sectionHeading("PROFILE"))
|
|
main.addArrangedSubview(sectionHeading("PROFILE"))
|
|
|
main.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 3))
|
|
main.addArrangedSubview(makeLabel(CVPreviewDemoContent.summary, font: .systemFont(ofSize: 6.2), color: palette.previewInk, alignment: .left, maxLines: 3))
|
|
|
main.addArrangedSubview(sectionHeading("IMPACT"))
|
|
main.addArrangedSubview(sectionHeading("IMPACT"))
|
|
|
main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.company) — \(CVPreviewDemoContent.experienceRole)", font: .systemFont(ofSize: 6.6, weight: .heavy), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.company) — \(CVPreviewDemoContent.experienceRole)", font: .systemFont(ofSize: 6.6, weight: .heavy), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
- let bMark = (idVariant % 2 == 0) ? "— " : "▸ "
|
|
|
|
|
|
|
+ let bMark = (layoutVariant % 2 == 0) ? "— " : "▸ "
|
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet1)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet1)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet2)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
main.addArrangedSubview(makeLabel("\(bMark)\(CVPreviewDemoContent.bullet2)", font: .systemFont(ofSize: 6), color: palette.previewMuted, alignment: .left, maxLines: 2))
|
|
|
main.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
main.addArrangedSubview(sectionHeading("EDUCATION"))
|
|
|
main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university) · \(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: .systemFont(ofSize: 6.1), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
main.addArrangedSubview(makeLabel("\(CVPreviewDemoContent.university) · \(CVPreviewDemoContent.degree) · \(CVPreviewDemoContent.educationYears)", font: .systemFont(ofSize: 6.1), color: palette.previewInk, alignment: .left, maxLines: 2))
|
|
|
|
|
|
|
|
- let sidebarMult = 0.32 + CGFloat(idVariant % 3) * 0.02
|
|
|
|
|
|
|
+ let sidebarMult = 0.32 + CGFloat(layoutVariant % 3) * 0.02
|
|
|
|
|
|
|
|
switch template.layout {
|
|
switch template.layout {
|
|
|
case .singleColumn:
|
|
case .singleColumn:
|
|
@@ -838,14 +819,14 @@ final class CVTemplatePreviewView: NSView {
|
|
|
banner.translatesAutoresizingMaskIntoConstraints = false
|
|
banner.translatesAutoresizingMaskIntoConstraints = false
|
|
|
banner.wantsLayer = true
|
|
banner.wantsLayer = true
|
|
|
banner.layer?.backgroundColor = theme.cgColor
|
|
banner.layer?.backgroundColor = theme.cgColor
|
|
|
- banner.layer?.cornerRadius = idVariant % 4 == 1 ? 6 : 3
|
|
|
|
|
|
|
+ banner.layer?.cornerRadius = layoutVariant % 4 == 1 ? 6 : 3
|
|
|
let inner = makeLabel(" \(CVPreviewDemoContent.fullName) · \(CVPreviewDemoContent.title)", font: .systemFont(ofSize: 6.5, weight: .bold), color: .white, alignment: .left, maxLines: 1)
|
|
let inner = makeLabel(" \(CVPreviewDemoContent.fullName) · \(CVPreviewDemoContent.title)", font: .systemFont(ofSize: 6.5, weight: .bold), color: .white, alignment: .left, maxLines: 1)
|
|
|
inner.translatesAutoresizingMaskIntoConstraints = false
|
|
inner.translatesAutoresizingMaskIntoConstraints = false
|
|
|
banner.addSubview(inner)
|
|
banner.addSubview(inner)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
inner.leadingAnchor.constraint(equalTo: banner.leadingAnchor, constant: 5),
|
|
inner.leadingAnchor.constraint(equalTo: banner.leadingAnchor, constant: 5),
|
|
|
inner.trailingAnchor.constraint(lessThanOrEqualTo: banner.trailingAnchor, constant: -5),
|
|
inner.trailingAnchor.constraint(lessThanOrEqualTo: banner.trailingAnchor, constant: -5),
|
|
|
- inner.topAnchor.constraint(equalTo: banner.topAnchor, constant: 4 + CGFloat(idVariant % 2)),
|
|
|
|
|
|
|
+ inner.topAnchor.constraint(equalTo: banner.topAnchor, constant: 4 + CGFloat(layoutVariant % 2)),
|
|
|
inner.bottomAnchor.constraint(equalTo: banner.bottomAnchor, constant: -4)
|
|
inner.bottomAnchor.constraint(equalTo: banner.bottomAnchor, constant: -4)
|
|
|
])
|
|
])
|
|
|
let col = NSStackView(views: [banner, main])
|
|
let col = NSStackView(views: [banner, main])
|
|
@@ -856,7 +837,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
case .twoColumn(let side, _):
|
|
case .twoColumn(let side, _):
|
|
|
let row = NSStackView()
|
|
let row = NSStackView()
|
|
|
row.orientation = .horizontal
|
|
row.orientation = .horizontal
|
|
|
- row.spacing = 5 + CGFloat(idVariant % 3)
|
|
|
|
|
|
|
+ row.spacing = 5 + CGFloat(layoutVariant % 3)
|
|
|
row.alignment = .top
|
|
row.alignment = .top
|
|
|
if side == .leading {
|
|
if side == .leading {
|
|
|
row.addArrangedSubview(sidebar)
|
|
row.addArrangedSubview(sidebar)
|
|
@@ -873,7 +854,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
private func creativeDeepBackground(theme: NSColor) -> NSColor {
|
|
private func creativeDeepBackground(theme: NSColor) -> NSColor {
|
|
|
let navy = NSColor(srgbRed: 0.08, green: 0.1, blue: 0.18, alpha: 1)
|
|
let navy = NSColor(srgbRed: 0.08, green: 0.1, blue: 0.18, alpha: 1)
|
|
|
let plum = NSColor(srgbRed: 0.14, green: 0.07, blue: 0.24, alpha: 1)
|
|
let plum = NSColor(srgbRed: 0.14, green: 0.07, blue: 0.24, alpha: 1)
|
|
|
- switch idVariant % 4 {
|
|
|
|
|
|
|
+ switch layoutVariant % 4 {
|
|
|
case 0: return theme.blended(withFraction: 0.52, of: navy) ?? theme
|
|
case 0: return theme.blended(withFraction: 0.52, of: navy) ?? theme
|
|
|
case 1: return theme.blended(withFraction: 0.7, of: NSColor.black) ?? theme
|
|
case 1: return theme.blended(withFraction: 0.7, of: NSColor.black) ?? theme
|
|
|
case 2: return palette.previewInk.blended(withFraction: 0.38, of: theme) ?? theme
|
|
case 2: return palette.previewInk.blended(withFraction: 0.38, of: theme) ?? theme
|
|
@@ -944,11 +925,7 @@ final class CVTemplatePreviewView: NSView {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func accentDecorationColor() -> NSColor {
|
|
private func accentDecorationColor() -> NSColor {
|
|
|
- switch template.accent {
|
|
|
|
|
- case .redUnderline, .redBar: return palette.previewAccentRed
|
|
|
|
|
- case .blueBar: return palette.previewAccentBlue
|
|
|
|
|
- case .none: return template.themeColor.blended(withFraction: 0.5, of: palette.previewInk) ?? palette.previewInk
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ CVResumeAppearance.accentColor(for: template)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func headlineAccent(theme: NSColor, width: CGFloat) -> NSView {
|
|
private func headlineAccent(theme: NSColor, width: CGFloat) -> NSView {
|