Просмотр исходного кода

Stabilize CV Maker filter chips and template card layout.

Equal-width filter rows, fixed chip slots, and uniform card chrome prevent layout shifts when switching category groups or selection state.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 недель назад: 3
Родитель
Сommit
14152a9801
1 измененных файлов с 86 добавлено и 39 удалено
  1. 86 39
      App for Indeed/Views/CVMakerPageView.swift

+ 86 - 39
App for Indeed/Views/CVMakerPageView.swift

@@ -629,6 +629,20 @@ final class CVMakerPageView: NSView {
629
 
629
 
630
     private var appliedGridColumnCount: Int = 0
630
     private var appliedGridColumnCount: Int = 0
631
 
631
 
632
+    /// Family filter row always renders this many slots so chip widths stay stable
633
+    /// when switching between Design-Based (3 labels) and Profession-Based (4).
634
+    private let familyChipSlotCount = 4
635
+
636
+    private enum FilterChromeLayout {
637
+        static let padding: CGFloat = 12
638
+        static let rowGap: CGFloat = 12
639
+        static let groupRowHeight: CGFloat = 38
640
+        static let familyRowHeight: CGFloat = 30
641
+        static var height: CGFloat {
642
+            padding * 2 + groupRowHeight + rowGap + familyRowHeight
643
+        }
644
+    }
645
+
632
     override init(frame frameRect: NSRect) {
646
     override init(frame frameRect: NSRect) {
633
         super.init(frame: frameRect)
647
         super.init(frame: frameRect)
634
         configureLayout()
648
         configureLayout()
@@ -681,7 +695,12 @@ final class CVMakerPageView: NSView {
681
             filterStack.leadingAnchor.constraint(equalTo: filterChrome.leadingAnchor, constant: 14),
695
             filterStack.leadingAnchor.constraint(equalTo: filterChrome.leadingAnchor, constant: 14),
682
             filterStack.trailingAnchor.constraint(equalTo: filterChrome.trailingAnchor, constant: -14),
696
             filterStack.trailingAnchor.constraint(equalTo: filterChrome.trailingAnchor, constant: -14),
683
             filterStack.topAnchor.constraint(equalTo: filterChrome.topAnchor, constant: 12),
697
             filterStack.topAnchor.constraint(equalTo: filterChrome.topAnchor, constant: 12),
684
-            filterStack.bottomAnchor.constraint(equalTo: filterChrome.bottomAnchor, constant: -12)
698
+            filterStack.bottomAnchor.constraint(equalTo: filterChrome.bottomAnchor, constant: -12),
699
+            // On this SDK `alignment` is `NSLayoutConstraint.Attribute` (no `.fill`).
700
+            // Pin row widths so group tabs stay full-width / equal split instead of
701
+            // shrinking to intrinsic width when selection changes.
702
+            groupTabsRow.widthAnchor.constraint(equalTo: filterStack.widthAnchor),
703
+            familyChipsRow.widthAnchor.constraint(equalTo: filterStack.widthAnchor)
685
         ])
704
         ])
686
 
705
 
687
         titleLabel.font = .systemFont(ofSize: 22, weight: .bold)
706
         titleLabel.font = .systemFont(ofSize: 22, weight: .bold)
@@ -704,13 +723,16 @@ final class CVMakerPageView: NSView {
704
         groupTabsRow.alignment = .centerY
723
         groupTabsRow.alignment = .centerY
705
         groupTabsRow.distribution = .fillEqually
724
         groupTabsRow.distribution = .fillEqually
706
         groupTabsRow.translatesAutoresizingMaskIntoConstraints = false
725
         groupTabsRow.translatesAutoresizingMaskIntoConstraints = false
726
+        groupTabsRow.heightAnchor.constraint(equalToConstant: 38).isActive = true
707
         configureGroupTabs()
727
         configureGroupTabs()
708
 
728
 
709
         familyChipsRow.orientation = .horizontal
729
         familyChipsRow.orientation = .horizontal
710
         familyChipsRow.spacing = 8
730
         familyChipsRow.spacing = 8
711
         familyChipsRow.alignment = .centerY
731
         familyChipsRow.alignment = .centerY
712
-        familyChipsRow.distribution = .fill
732
+        // Match the top row: equal-width segments, evenly spaced across the row.
733
+        familyChipsRow.distribution = .fillEqually
713
         familyChipsRow.translatesAutoresizingMaskIntoConstraints = false
734
         familyChipsRow.translatesAutoresizingMaskIntoConstraints = false
735
+        familyChipsRow.heightAnchor.constraint(equalToConstant: 30).isActive = true
714
 
736
 
715
         gridStack.orientation = .vertical
737
         gridStack.orientation = .vertical
716
         gridStack.spacing = 26
738
         gridStack.spacing = 26
@@ -759,8 +781,9 @@ final class CVMakerPageView: NSView {
759
             headerStack.topAnchor.constraint(equalTo: topAnchor, constant: 8),
781
             headerStack.topAnchor.constraint(equalTo: topAnchor, constant: 8),
760
 
782
 
761
             filterChrome.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalInset),
783
             filterChrome.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalInset),
762
-            filterChrome.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -horizontalInset),
784
+            filterChrome.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horizontalInset),
763
             filterChrome.topAnchor.constraint(equalTo: headerStack.bottomAnchor, constant: 16),
785
             filterChrome.topAnchor.constraint(equalTo: headerStack.bottomAnchor, constant: 16),
786
+            filterChrome.heightAnchor.constraint(equalToConstant: FilterChromeLayout.height),
764
 
787
 
765
             scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalInset),
788
             scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalInset),
766
             scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horizontalInset),
789
             scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horizontalInset),
@@ -808,6 +831,7 @@ final class CVMakerPageView: NSView {
808
 
831
 
809
         let allCount = templates(forGroup: selectedGroup, family: nil).count
832
         let allCount = templates(forGroup: selectedGroup, family: nil).count
810
         let allChip = CVChipButton(title: "All", badgeText: "\(allCount)", leadingSymbol: nil, style: .pillSmall)
833
         let allChip = CVChipButton(title: "All", badgeText: "\(allCount)", leadingSymbol: nil, style: .pillSmall)
834
+        allChip.translatesAutoresizingMaskIntoConstraints = false
811
         allChip.onSelect = { [weak self] in self?.didSelectFamily(nil) }
835
         allChip.onSelect = { [weak self] in self?.didSelectFamily(nil) }
812
         familyChipsRow.addArrangedSubview(allChip)
836
         familyChipsRow.addArrangedSubview(allChip)
813
         familyChipButtons[nil] = allChip
837
         familyChipButtons[nil] = allChip
@@ -816,16 +840,21 @@ final class CVMakerPageView: NSView {
816
             let count = templates(forGroup: selectedGroup, family: family).count
840
             let count = templates(forGroup: selectedGroup, family: family).count
817
             guard count > 0 else { continue }
841
             guard count > 0 else { continue }
818
             let chip = CVChipButton(title: family.title, badgeText: "\(count)", leadingSymbol: nil, style: .pillSmall)
842
             let chip = CVChipButton(title: family.title, badgeText: "\(count)", leadingSymbol: nil, style: .pillSmall)
843
+            chip.translatesAutoresizingMaskIntoConstraints = false
819
             chip.onSelect = { [weak self] in self?.didSelectFamily(family) }
844
             chip.onSelect = { [weak self] in self?.didSelectFamily(family) }
820
             familyChipsRow.addArrangedSubview(chip)
845
             familyChipsRow.addArrangedSubview(chip)
821
             familyChipButtons[family] = chip
846
             familyChipButtons[family] = chip
822
         }
847
         }
823
 
848
 
824
-        let familyRowTailSpacer = NSView()
825
-        familyRowTailSpacer.translatesAutoresizingMaskIntoConstraints = false
826
-        familyRowTailSpacer.setContentHuggingPriority(.init(1), for: .horizontal)
827
-        familyRowTailSpacer.setContentCompressionResistancePriority(.init(1), for: .horizontal)
828
-        familyChipsRow.addArrangedSubview(familyRowTailSpacer)
849
+        // Pad to a fixed slot count so `fillEqually` chip widths never change when
850
+        // the visible family count differs between category groups.
851
+        while familyChipsRow.arrangedSubviews.count < familyChipSlotCount {
852
+            let slot = NSView()
853
+            slot.translatesAutoresizingMaskIntoConstraints = false
854
+            slot.setContentHuggingPriority(.defaultLow, for: .horizontal)
855
+            slot.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
856
+            familyChipsRow.addArrangedSubview(slot)
857
+        }
829
 
858
 
830
         if let f = selectedFamily, templates(forGroup: selectedGroup, family: f).isEmpty {
859
         if let f = selectedFamily, templates(forGroup: selectedGroup, family: f).isEmpty {
831
             selectedFamily = nil
860
             selectedFamily = nil
@@ -890,10 +919,12 @@ final class CVMakerPageView: NSView {
890
                     let filler = NSView()
919
                     let filler = NSView()
891
                     filler.translatesAutoresizingMaskIntoConstraints = false
920
                     filler.translatesAutoresizingMaskIntoConstraints = false
892
                     row.addArrangedSubview(filler)
921
                     row.addArrangedSubview(filler)
922
+                    filler.heightAnchor.constraint(equalToConstant: CVTemplateCard.layoutHeight).isActive = true
893
                 }
923
                 }
894
             }
924
             }
895
             gridStack.addArrangedSubview(row)
925
             gridStack.addArrangedSubview(row)
896
             row.widthAnchor.constraint(equalTo: gridStack.widthAnchor).isActive = true
926
             row.widthAnchor.constraint(equalTo: gridStack.widthAnchor).isActive = true
927
+            row.heightAnchor.constraint(equalToConstant: CVTemplateCard.layoutHeight).isActive = true
897
             index += columns
928
             index += columns
898
         }
929
         }
899
 
930
 
@@ -1090,11 +1121,12 @@ private final class CVChipButton: NSView {
1090
     private var trackingArea: NSTrackingArea?
1121
     private var trackingArea: NSTrackingArea?
1091
 
1122
 
1092
     private enum Palette {
1123
     private enum Palette {
1093
-        static let restFill = NSColor(srgbRed: 247 / 255, green: 249 / 255, blue: 252 / 255, alpha: 1)
1124
+        static let restFill = NSColor.white
1094
         static let restBorder = NSColor(srgbRed: 222 / 255, green: 226 / 255, blue: 233 / 255, alpha: 1)
1125
         static let restBorder = NSColor(srgbRed: 222 / 255, green: 226 / 255, blue: 233 / 255, alpha: 1)
1095
-        static let hoverFill = NSColor(srgbRed: 238 / 255, green: 241 / 255, blue: 247 / 255, alpha: 1)
1126
+        static let hoverFill = NSColor(srgbRed: 248 / 255, green: 250 / 255, blue: 252 / 255, alpha: 1)
1096
         static let activeFill = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
1127
         static let activeFill = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
1097
         static let activeFillHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
1128
         static let activeFillHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
1129
+        static let activeBorder = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
1098
         static let restText = NSColor(srgbRed: 71 / 255, green: 85 / 255, blue: 105 / 255, alpha: 1)
1130
         static let restText = NSColor(srgbRed: 71 / 255, green: 85 / 255, blue: 105 / 255, alpha: 1)
1099
         static let activeText = NSColor.white
1131
         static let activeText = NSColor.white
1100
         static let restBadge = NSColor(srgbRed: 230 / 255, green: 234 / 255, blue: 240 / 255, alpha: 1)
1132
         static let restBadge = NSColor(srgbRed: 230 / 255, green: 234 / 255, blue: 240 / 255, alpha: 1)
@@ -1103,6 +1135,10 @@ private final class CVChipButton: NSView {
1103
         static let activeBadgeText = NSColor.white
1135
         static let activeBadgeText = NSColor.white
1104
     }
1136
     }
1105
 
1137
 
1138
+    private static let symbolSide: CGFloat = 18
1139
+    private static let badgeWidthLarge: CGFloat = 28
1140
+    private static let badgeWidthSmall: CGFloat = 26
1141
+
1106
     init(title: String, badgeText: String, leadingSymbol: String?, style: Style) {
1142
     init(title: String, badgeText: String, leadingSymbol: String?, style: Style) {
1107
         self.style = style
1143
         self.style = style
1108
         super.init(frame: .zero)
1144
         super.init(frame: .zero)
@@ -1116,13 +1152,16 @@ private final class CVChipButton: NSView {
1116
 
1152
 
1117
         titleLabel.stringValue = title
1153
         titleLabel.stringValue = title
1118
         titleLabel.font = .systemFont(ofSize: style == .pillLarge ? 13 : 12, weight: .semibold)
1154
         titleLabel.font = .systemFont(ofSize: style == .pillLarge ? 13 : 12, weight: .semibold)
1155
+        titleLabel.maximumNumberOfLines = 1
1156
+        titleLabel.lineBreakMode = .byTruncatingTail
1157
+        titleLabel.cell?.lineBreakMode = .byTruncatingTail
1119
         titleLabel.isBordered = false
1158
         titleLabel.isBordered = false
1120
         titleLabel.drawsBackground = false
1159
         titleLabel.drawsBackground = false
1121
         titleLabel.isEditable = false
1160
         titleLabel.isEditable = false
1122
         titleLabel.isSelectable = false
1161
         titleLabel.isSelectable = false
1123
 
1162
 
1124
         badgeLabel.stringValue = badgeText
1163
         badgeLabel.stringValue = badgeText
1125
-        badgeLabel.font = .systemFont(ofSize: 10.5, weight: .semibold)
1164
+        badgeLabel.font = .monospacedDigitSystemFont(ofSize: 10.5, weight: .semibold)
1126
         badgeLabel.alignment = .center
1165
         badgeLabel.alignment = .center
1127
         badgeLabel.isBordered = false
1166
         badgeLabel.isBordered = false
1128
         badgeLabel.drawsBackground = false
1167
         badgeLabel.drawsBackground = false
@@ -1139,7 +1178,9 @@ private final class CVChipButton: NSView {
1139
             badgeLabel.trailingAnchor.constraint(equalTo: badgePill.trailingAnchor, constant: -7),
1178
             badgeLabel.trailingAnchor.constraint(equalTo: badgePill.trailingAnchor, constant: -7),
1140
             badgeLabel.centerYAnchor.constraint(equalTo: badgePill.centerYAnchor),
1179
             badgeLabel.centerYAnchor.constraint(equalTo: badgePill.centerYAnchor),
1141
             badgePill.heightAnchor.constraint(equalToConstant: 18),
1180
             badgePill.heightAnchor.constraint(equalToConstant: 18),
1142
-            badgePill.widthAnchor.constraint(greaterThanOrEqualToConstant: 22)
1181
+            badgePill.widthAnchor.constraint(
1182
+                equalToConstant: style == .pillLarge ? Self.badgeWidthLarge : Self.badgeWidthSmall
1183
+            )
1143
         ])
1184
         ])
1144
 
1185
 
1145
         stack.orientation = .horizontal
1186
         stack.orientation = .horizontal
@@ -1151,30 +1192,38 @@ private final class CVChipButton: NSView {
1151
             symbolView.translatesAutoresizingMaskIntoConstraints = false
1192
             symbolView.translatesAutoresizingMaskIntoConstraints = false
1152
             symbolView.image = NSImage(systemSymbolName: symbol, accessibilityDescription: nil)
1193
             symbolView.image = NSImage(systemSymbolName: symbol, accessibilityDescription: nil)
1153
             symbolView.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 13, weight: .semibold)
1194
             symbolView.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 13, weight: .semibold)
1195
+            symbolView.setContentHuggingPriority(.required, for: .horizontal)
1196
+            symbolView.setContentCompressionResistancePriority(.required, for: .horizontal)
1154
             stack.addArrangedSubview(symbolView)
1197
             stack.addArrangedSubview(symbolView)
1198
+            NSLayoutConstraint.activate([
1199
+                symbolView.widthAnchor.constraint(equalToConstant: Self.symbolSide),
1200
+                symbolView.heightAnchor.constraint(equalToConstant: Self.symbolSide)
1201
+            ])
1155
         }
1202
         }
1156
         stack.addArrangedSubview(titleLabel)
1203
         stack.addArrangedSubview(titleLabel)
1157
         stack.addArrangedSubview(badgePill)
1204
         stack.addArrangedSubview(badgePill)
1158
 
1205
 
1159
         addSubview(stack)
1206
         addSubview(stack)
1160
         let horizontalInset: CGFloat = style == .pillLarge ? 16 : 12
1207
         let horizontalInset: CGFloat = style == .pillLarge ? 16 : 12
1208
+        // Leading alignment for every chip so selected vs unselected pills share the
1209
+        // same geometry (centered stacks shift when badge digits change).
1161
         if style == .pillLarge {
1210
         if style == .pillLarge {
1162
-            // Group toggle shares a row: equal widths from the parent stack; keep
1163
-            // icon + label + badge centered so selection state does not reshuffle width.
1164
             NSLayoutConstraint.activate([
1211
             NSLayoutConstraint.activate([
1165
-                stack.centerXAnchor.constraint(equalTo: centerXAnchor),
1166
-                stack.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: horizontalInset),
1212
+                stack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalInset),
1167
                 stack.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -horizontalInset),
1213
                 stack.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -horizontalInset),
1168
                 stack.centerYAnchor.constraint(equalTo: centerYAnchor)
1214
                 stack.centerYAnchor.constraint(equalTo: centerYAnchor)
1169
             ])
1215
             ])
1170
             setContentHuggingPriority(.defaultLow, for: .horizontal)
1216
             setContentHuggingPriority(.defaultLow, for: .horizontal)
1171
         } else {
1217
         } else {
1218
+            // Parent row uses `fillEqually`; center label + badge inside each segment.
1172
             NSLayoutConstraint.activate([
1219
             NSLayoutConstraint.activate([
1173
-                stack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalInset),
1174
-                stack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horizontalInset),
1220
+                stack.centerXAnchor.constraint(equalTo: centerXAnchor),
1221
+                stack.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: horizontalInset),
1222
+                stack.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -horizontalInset),
1175
                 stack.centerYAnchor.constraint(equalTo: centerYAnchor)
1223
                 stack.centerYAnchor.constraint(equalTo: centerYAnchor)
1176
             ])
1224
             ])
1177
-            setContentHuggingPriority(.required, for: .horizontal)
1225
+            setContentHuggingPriority(.defaultLow, for: .horizontal)
1226
+            setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
1178
         }
1227
         }
1179
         applyState()
1228
         applyState()
1180
     }
1229
     }
@@ -1244,7 +1293,7 @@ private final class CVChipButton: NSView {
1244
         let badgeText: NSColor
1293
         let badgeText: NSColor
1245
         if isSelected {
1294
         if isSelected {
1246
             fill = isHovering ? Palette.activeFillHover : Palette.activeFill
1295
             fill = isHovering ? Palette.activeFillHover : Palette.activeFill
1247
-            border = fill
1296
+            border = Palette.activeBorder
1248
             textColor = Palette.activeText
1297
             textColor = Palette.activeText
1249
             badgeFill = Palette.activeBadge
1298
             badgeFill = Palette.activeBadge
1250
             badgeText = Palette.activeBadgeText
1299
             badgeText = Palette.activeBadgeText
@@ -1257,6 +1306,7 @@ private final class CVChipButton: NSView {
1257
         }
1306
         }
1258
         layer?.backgroundColor = fill.cgColor
1307
         layer?.backgroundColor = fill.cgColor
1259
         layer?.borderColor = border.cgColor
1308
         layer?.borderColor = border.cgColor
1309
+        layer?.borderWidth = 1
1260
         titleLabel.textColor = textColor
1310
         titleLabel.textColor = textColor
1261
         symbolView.contentTintColor = textColor
1311
         symbolView.contentTintColor = textColor
1262
         badgePill.layer?.backgroundColor = badgeFill.cgColor
1312
         badgePill.layer?.backgroundColor = badgeFill.cgColor
@@ -1269,6 +1319,8 @@ private final class CVChipButton: NSView {
1269
 /// Premium gallery card: live résumé thumbnail, soft shadow, and an animated
1319
 /// Premium gallery card: live résumé thumbnail, soft shadow, and an animated
1270
 /// brand border when selected.
1320
 /// brand border when selected.
1271
 private final class CVTemplateCard: NSView {
1321
 private final class CVTemplateCard: NSView {
1322
+    static let layoutHeight: CGFloat = 292
1323
+
1272
     var onSelect: (() -> Void)?
1324
     var onSelect: (() -> Void)?
1273
     var isSelected: Bool = false { didSet { applyChrome() } }
1325
     var isSelected: Bool = false { didSet { applyChrome() } }
1274
     /// Distinguishes this card from others that may share the same catalog `template.id`.
1326
     /// Distinguishes this card from others that may share the same catalog `template.id`.
@@ -1296,7 +1348,7 @@ private final class CVTemplateCard: NSView {
1296
         layer?.cornerRadius = 24
1348
         layer?.cornerRadius = 24
1297
         layer?.backgroundColor = NSColor.white.cgColor
1349
         layer?.backgroundColor = NSColor.white.cgColor
1298
         translatesAutoresizingMaskIntoConstraints = false
1350
         translatesAutoresizingMaskIntoConstraints = false
1299
-        heightAnchor.constraint(greaterThanOrEqualToConstant: 292).isActive = true
1351
+        heightAnchor.constraint(equalToConstant: Self.layoutHeight).isActive = true
1300
 
1352
 
1301
         previewSurface.translatesAutoresizingMaskIntoConstraints = false
1353
         previewSurface.translatesAutoresizingMaskIntoConstraints = false
1302
         previewSurface.wantsLayer = true
1354
         previewSurface.wantsLayer = true
@@ -1436,28 +1488,23 @@ private final class CVTemplateCard: NSView {
1436
     }
1488
     }
1437
 
1489
 
1438
     private func applyChrome() {
1490
     private func applyChrome() {
1491
+        // Same border + shadow metrics in every state — only color changes on select
1492
+        // so thumbnails keep identical insets (stroke is drawn inside the layer).
1493
+        let uniformBorder: CGFloat = 2
1494
+        let borderColor: NSColor
1439
         if isSelected {
1495
         if isSelected {
1440
-            layer?.borderColor = palette.borderSelected.cgColor
1441
-            layer?.borderWidth = 2.5
1442
-            layer?.shadowColor = palette.borderSelected.cgColor
1443
-            layer?.shadowOpacity = 0.42
1444
-            layer?.shadowRadius = 22
1445
-            layer?.shadowOffset = CGSize(width: 0, height: 10)
1496
+            borderColor = palette.borderSelected
1446
         } else if isHovering {
1497
         } else if isHovering {
1447
-            layer?.borderColor = palette.borderHover.cgColor
1448
-            layer?.borderWidth = 1.5
1449
-            layer?.shadowColor = NSColor.black.cgColor
1450
-            layer?.shadowOpacity = 0.14
1451
-            layer?.shadowRadius = 18
1452
-            layer?.shadowOffset = CGSize(width: 0, height: 10)
1498
+            borderColor = palette.borderHover
1453
         } else {
1499
         } else {
1454
-            layer?.borderColor = palette.border.cgColor
1455
-            layer?.borderWidth = 1
1456
-            layer?.shadowColor = NSColor.black.cgColor
1457
-            layer?.shadowOpacity = 0.08
1458
-            layer?.shadowRadius = 12
1459
-            layer?.shadowOffset = CGSize(width: 0, height: 6)
1500
+            borderColor = palette.border
1460
         }
1501
         }
1502
+        layer?.borderColor = borderColor.cgColor
1503
+        layer?.borderWidth = uniformBorder
1504
+        layer?.shadowColor = NSColor.black.cgColor
1505
+        layer?.shadowOpacity = isSelected ? 0.14 : (isHovering ? 0.11 : 0.08)
1506
+        layer?.shadowRadius = 14
1507
+        layer?.shadowOffset = CGSize(width: 0, height: 8)
1461
     }
1508
     }
1462
 }
1509
 }
1463
 
1510