|
|
@@ -124,6 +124,15 @@ struct CVTemplate: Hashable {
|
|
124
|
124
|
/// User-facing template title for the active language (`name` is the English localization key).
|
|
125
|
125
|
var localizedName: String { localizedTemplateName(name) }
|
|
126
|
126
|
|
|
|
127
|
+ /// Non-empty label for gallery cards and profile rows.
|
|
|
128
|
+ var displayName: String {
|
|
|
129
|
+ let localized = localizedName.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
130
|
+ if !localized.isEmpty { return localized }
|
|
|
131
|
+ let raw = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
132
|
+ if !raw.isEmpty { return raw }
|
|
|
133
|
+ return id
|
|
|
134
|
+ }
|
|
|
135
|
+
|
|
127
|
136
|
/// Optional bundle image name; `nil` means render a live vector/text preview.
|
|
128
|
137
|
var previewImageAssetName: String? { nil }
|
|
129
|
138
|
|
|
|
@@ -1417,7 +1426,9 @@ private final class CVChipButton: NSView {
|
|
1417
|
1426
|
/// Premium gallery card: live résumé thumbnail, soft shadow, and an animated
|
|
1418
|
1427
|
/// brand border when selected.
|
|
1419
|
1428
|
private final class CVTemplateCard: NSView {
|
|
1420
|
|
- static let layoutHeight: CGFloat = 292
|
|
|
1429
|
+ private static let footerHeight: CGFloat = 50
|
|
|
1430
|
+ private static let previewHeight: CGFloat = 242
|
|
|
1431
|
+ static let layoutHeight: CGFloat = footerHeight + previewHeight
|
|
1421
|
1432
|
|
|
1422
|
1433
|
var onSelect: (() -> Void)?
|
|
1423
|
1434
|
var isSelected: Bool = false { didSet { applyChrome() } }
|
|
|
@@ -1432,7 +1443,6 @@ private final class CVTemplateCard: NSView {
|
|
1432
|
1443
|
private let footerView = NSView()
|
|
1433
|
1444
|
private let preview: CVTemplatePreviewView
|
|
1434
|
1445
|
private let nameLabel = NSTextField(labelWithString: "")
|
|
1435
|
|
- private let categoryLabel = NSTextField(labelWithString: "")
|
|
1436
|
1446
|
private var trackingArea: NSTrackingArea?
|
|
1437
|
1447
|
private var isHovering: Bool = false
|
|
1438
|
1448
|
private var didPushCursor: Bool = false
|
|
|
@@ -1452,42 +1462,43 @@ private final class CVTemplateCard: NSView {
|
|
1452
|
1462
|
previewSurface.translatesAutoresizingMaskIntoConstraints = false
|
|
1453
|
1463
|
previewSurface.wantsLayer = true
|
|
1454
|
1464
|
previewSurface.layer?.backgroundColor = palette.previewSurface.cgColor
|
|
|
1465
|
+ previewSurface.layer?.masksToBounds = true
|
|
1455
|
1466
|
|
|
1456
|
1467
|
preview.translatesAutoresizingMaskIntoConstraints = false
|
|
1457
|
1468
|
previewSurface.addSubview(preview)
|
|
1458
|
1469
|
|
|
1459
|
|
- refreshLocalizedTitle()
|
|
1460
|
1470
|
nameLabel.font = .systemFont(ofSize: 14, weight: .semibold)
|
|
1461
|
1471
|
nameLabel.textColor = palette.primaryText
|
|
1462
|
1472
|
nameLabel.isBordered = false
|
|
1463
|
1473
|
nameLabel.drawsBackground = false
|
|
1464
|
1474
|
nameLabel.isEditable = false
|
|
1465
|
1475
|
nameLabel.isSelectable = false
|
|
|
1476
|
+ nameLabel.lineBreakMode = .byTruncatingTail
|
|
|
1477
|
+ nameLabel.maximumNumberOfLines = 1
|
|
|
1478
|
+ nameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
|
|
1479
|
+ nameLabel.setContentHuggingPriority(.required, for: .vertical)
|
|
|
1480
|
+ refreshLocalizedTitle()
|
|
1466
|
1481
|
|
|
1467
|
|
- categoryLabel.font = .systemFont(ofSize: 11.5, weight: .regular)
|
|
1468
|
|
- categoryLabel.textColor = palette.secondaryText
|
|
1469
|
|
- categoryLabel.isBordered = false
|
|
1470
|
|
- categoryLabel.drawsBackground = false
|
|
1471
|
|
- categoryLabel.isEditable = false
|
|
1472
|
|
- categoryLabel.isSelectable = false
|
|
1473
|
|
-
|
|
1474
|
|
- let footerStack = NSStackView(views: [nameLabel, categoryLabel])
|
|
|
1482
|
+ let footerStack = NSStackView(views: [nameLabel])
|
|
1475
|
1483
|
footerStack.orientation = .vertical
|
|
1476
|
|
- footerStack.spacing = 3
|
|
1477
|
1484
|
footerStack.alignment = .leading
|
|
1478
|
1485
|
footerStack.translatesAutoresizingMaskIntoConstraints = false
|
|
1479
|
1486
|
|
|
1480
|
1487
|
footerView.translatesAutoresizingMaskIntoConstraints = false
|
|
1481
|
1488
|
footerView.wantsLayer = true
|
|
1482
|
1489
|
footerView.layer?.backgroundColor = palette.footerBackground.cgColor
|
|
|
1490
|
+ footerView.setContentCompressionResistancePriority(.required, for: .vertical)
|
|
|
1491
|
+ footerView.setContentHuggingPriority(.required, for: .vertical)
|
|
1483
|
1492
|
footerView.addSubview(footerStack)
|
|
1484
|
1493
|
NSLayoutConstraint.activate([
|
|
1485
|
1494
|
footerStack.leadingAnchor.constraint(equalTo: footerView.leadingAnchor, constant: 16),
|
|
1486
|
1495
|
footerStack.trailingAnchor.constraint(lessThanOrEqualTo: footerView.trailingAnchor, constant: -16),
|
|
1487
|
|
- footerStack.topAnchor.constraint(equalTo: footerView.topAnchor, constant: 13),
|
|
1488
|
|
- footerStack.bottomAnchor.constraint(equalTo: footerView.bottomAnchor, constant: -13)
|
|
|
1496
|
+ footerStack.centerYAnchor.constraint(equalTo: footerView.centerYAnchor)
|
|
1489
|
1497
|
])
|
|
1490
|
1498
|
|
|
|
1499
|
+ previewSurface.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
|
|
1500
|
+ previewSurface.setContentHuggingPriority(.defaultLow, for: .vertical)
|
|
|
1501
|
+
|
|
1491
|
1502
|
addSubview(previewSurface)
|
|
1492
|
1503
|
addSubview(footerView)
|
|
1493
|
1504
|
|
|
|
@@ -1495,7 +1506,7 @@ private final class CVTemplateCard: NSView {
|
|
1495
|
1506
|
previewSurface.topAnchor.constraint(equalTo: topAnchor),
|
|
1496
|
1507
|
previewSurface.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
1497
|
1508
|
previewSurface.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
1498
|
|
- previewSurface.heightAnchor.constraint(greaterThanOrEqualToConstant: 236),
|
|
|
1509
|
+ previewSurface.heightAnchor.constraint(equalToConstant: Self.previewHeight),
|
|
1499
|
1510
|
|
|
1500
|
1511
|
preview.topAnchor.constraint(equalTo: previewSurface.topAnchor, constant: 14),
|
|
1501
|
1512
|
preview.leadingAnchor.constraint(equalTo: previewSurface.leadingAnchor, constant: 16),
|
|
|
@@ -1505,7 +1516,8 @@ private final class CVTemplateCard: NSView {
|
|
1505
|
1516
|
footerView.topAnchor.constraint(equalTo: previewSurface.bottomAnchor),
|
|
1506
|
1517
|
footerView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
1507
|
1518
|
footerView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
1508
|
|
- footerView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
|
|
1519
|
+ footerView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
|
1520
|
+ footerView.heightAnchor.constraint(equalToConstant: Self.footerHeight)
|
|
1509
|
1521
|
])
|
|
1510
|
1522
|
applyChrome()
|
|
1511
|
1523
|
}
|
|
|
@@ -1516,8 +1528,7 @@ private final class CVTemplateCard: NSView {
|
|
1516
|
1528
|
}
|
|
1517
|
1529
|
|
|
1518
|
1530
|
func refreshLocalizedTitle() {
|
|
1519
|
|
- nameLabel.stringValue = template.localizedName
|
|
1520
|
|
- categoryLabel.stringValue = "\(template.category) · \(template.layoutType.gallerySubtitle)"
|
|
|
1531
|
+ nameLabel.stringValue = template.displayName
|
|
1521
|
1532
|
}
|
|
1522
|
1533
|
|
|
1523
|
1534
|
override func layout() {
|
|
|
@@ -1605,7 +1616,6 @@ private final class CVTemplateCard: NSView {
|
|
1605
|
1616
|
previewSurface.layer?.backgroundColor = palette.previewSurface.cgColor
|
|
1606
|
1617
|
footerView.layer?.backgroundColor = palette.footerBackground.cgColor
|
|
1607
|
1618
|
nameLabel.textColor = palette.primaryText
|
|
1608
|
|
- categoryLabel.textColor = palette.secondaryText
|
|
1609
|
1619
|
layer?.borderColor = borderColor.cgColor
|
|
1610
|
1620
|
layer?.borderWidth = uniformBorder
|
|
1611
|
1621
|
layer?.shadowColor = NSColor.black.cgColor
|