|
|
@@ -124,6 +124,15 @@ struct CVTemplate: Hashable {
|
|
|
/// User-facing template title for the active language (`name` is the English localization key).
|
|
|
var localizedName: String { localizedTemplateName(name) }
|
|
|
|
|
|
+ /// Non-empty label for gallery cards and profile rows.
|
|
|
+ var displayName: String {
|
|
|
+ let localized = localizedName.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
+ if !localized.isEmpty { return localized }
|
|
|
+ let raw = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
+ if !raw.isEmpty { return raw }
|
|
|
+ return id
|
|
|
+ }
|
|
|
+
|
|
|
/// Optional bundle image name; `nil` means render a live vector/text preview.
|
|
|
var previewImageAssetName: String? { nil }
|
|
|
|
|
|
@@ -1417,7 +1426,9 @@ private final class CVChipButton: NSView {
|
|
|
/// Premium gallery card: live résumé thumbnail, soft shadow, and an animated
|
|
|
/// brand border when selected.
|
|
|
private final class CVTemplateCard: NSView {
|
|
|
- static let layoutHeight: CGFloat = 292
|
|
|
+ private static let footerHeight: CGFloat = 50
|
|
|
+ private static let previewHeight: CGFloat = 242
|
|
|
+ static let layoutHeight: CGFloat = footerHeight + previewHeight
|
|
|
|
|
|
var onSelect: (() -> Void)?
|
|
|
var isSelected: Bool = false { didSet { applyChrome() } }
|
|
|
@@ -1432,7 +1443,6 @@ private final class CVTemplateCard: NSView {
|
|
|
private let footerView = NSView()
|
|
|
private let preview: CVTemplatePreviewView
|
|
|
private let nameLabel = NSTextField(labelWithString: "")
|
|
|
- private let categoryLabel = NSTextField(labelWithString: "")
|
|
|
private var trackingArea: NSTrackingArea?
|
|
|
private var isHovering: Bool = false
|
|
|
private var didPushCursor: Bool = false
|
|
|
@@ -1452,42 +1462,43 @@ private final class CVTemplateCard: NSView {
|
|
|
previewSurface.translatesAutoresizingMaskIntoConstraints = false
|
|
|
previewSurface.wantsLayer = true
|
|
|
previewSurface.layer?.backgroundColor = palette.previewSurface.cgColor
|
|
|
+ previewSurface.layer?.masksToBounds = true
|
|
|
|
|
|
preview.translatesAutoresizingMaskIntoConstraints = false
|
|
|
previewSurface.addSubview(preview)
|
|
|
|
|
|
- refreshLocalizedTitle()
|
|
|
nameLabel.font = .systemFont(ofSize: 14, weight: .semibold)
|
|
|
nameLabel.textColor = palette.primaryText
|
|
|
nameLabel.isBordered = false
|
|
|
nameLabel.drawsBackground = false
|
|
|
nameLabel.isEditable = false
|
|
|
nameLabel.isSelectable = false
|
|
|
+ nameLabel.lineBreakMode = .byTruncatingTail
|
|
|
+ nameLabel.maximumNumberOfLines = 1
|
|
|
+ nameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
|
|
+ nameLabel.setContentHuggingPriority(.required, for: .vertical)
|
|
|
+ refreshLocalizedTitle()
|
|
|
|
|
|
- categoryLabel.font = .systemFont(ofSize: 11.5, weight: .regular)
|
|
|
- categoryLabel.textColor = palette.secondaryText
|
|
|
- categoryLabel.isBordered = false
|
|
|
- categoryLabel.drawsBackground = false
|
|
|
- categoryLabel.isEditable = false
|
|
|
- categoryLabel.isSelectable = false
|
|
|
-
|
|
|
- let footerStack = NSStackView(views: [nameLabel, categoryLabel])
|
|
|
+ let footerStack = NSStackView(views: [nameLabel])
|
|
|
footerStack.orientation = .vertical
|
|
|
- footerStack.spacing = 3
|
|
|
footerStack.alignment = .leading
|
|
|
footerStack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
footerView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
footerView.wantsLayer = true
|
|
|
footerView.layer?.backgroundColor = palette.footerBackground.cgColor
|
|
|
+ footerView.setContentCompressionResistancePriority(.required, for: .vertical)
|
|
|
+ footerView.setContentHuggingPriority(.required, for: .vertical)
|
|
|
footerView.addSubview(footerStack)
|
|
|
NSLayoutConstraint.activate([
|
|
|
footerStack.leadingAnchor.constraint(equalTo: footerView.leadingAnchor, constant: 16),
|
|
|
footerStack.trailingAnchor.constraint(lessThanOrEqualTo: footerView.trailingAnchor, constant: -16),
|
|
|
- footerStack.topAnchor.constraint(equalTo: footerView.topAnchor, constant: 13),
|
|
|
- footerStack.bottomAnchor.constraint(equalTo: footerView.bottomAnchor, constant: -13)
|
|
|
+ footerStack.centerYAnchor.constraint(equalTo: footerView.centerYAnchor)
|
|
|
])
|
|
|
|
|
|
+ previewSurface.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
|
|
+ previewSurface.setContentHuggingPriority(.defaultLow, for: .vertical)
|
|
|
+
|
|
|
addSubview(previewSurface)
|
|
|
addSubview(footerView)
|
|
|
|
|
|
@@ -1495,7 +1506,7 @@ private final class CVTemplateCard: NSView {
|
|
|
previewSurface.topAnchor.constraint(equalTo: topAnchor),
|
|
|
previewSurface.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
|
previewSurface.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
|
- previewSurface.heightAnchor.constraint(greaterThanOrEqualToConstant: 236),
|
|
|
+ previewSurface.heightAnchor.constraint(equalToConstant: Self.previewHeight),
|
|
|
|
|
|
preview.topAnchor.constraint(equalTo: previewSurface.topAnchor, constant: 14),
|
|
|
preview.leadingAnchor.constraint(equalTo: previewSurface.leadingAnchor, constant: 16),
|
|
|
@@ -1505,7 +1516,8 @@ private final class CVTemplateCard: NSView {
|
|
|
footerView.topAnchor.constraint(equalTo: previewSurface.bottomAnchor),
|
|
|
footerView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
|
footerView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
|
- footerView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
|
|
+ footerView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
|
+ footerView.heightAnchor.constraint(equalToConstant: Self.footerHeight)
|
|
|
])
|
|
|
applyChrome()
|
|
|
}
|
|
|
@@ -1516,8 +1528,7 @@ private final class CVTemplateCard: NSView {
|
|
|
}
|
|
|
|
|
|
func refreshLocalizedTitle() {
|
|
|
- nameLabel.stringValue = template.localizedName
|
|
|
- categoryLabel.stringValue = "\(template.category) · \(template.layoutType.gallerySubtitle)"
|
|
|
+ nameLabel.stringValue = template.displayName
|
|
|
}
|
|
|
|
|
|
override func layout() {
|
|
|
@@ -1605,7 +1616,6 @@ private final class CVTemplateCard: NSView {
|
|
|
previewSurface.layer?.backgroundColor = palette.previewSurface.cgColor
|
|
|
footerView.layer?.backgroundColor = palette.footerBackground.cgColor
|
|
|
nameLabel.textColor = palette.primaryText
|
|
|
- categoryLabel.textColor = palette.secondaryText
|
|
|
layer?.borderColor = borderColor.cgColor
|
|
|
layer?.borderWidth = uniformBorder
|
|
|
layer?.shadowColor = NSColor.black.cgColor
|