Kaynağa Gözat

Polish Profiles add-button layout and padding

Improve the Add new profile pill: SF Symbol trailing arrow, pill corner
radius from height, intrinsic padding with extra trailing room and slack
so the arrow is not clipped by the capsule mask, horizontal hugging so
the control is not stretched edge-to-edge in the footer stack, and
compression resistance so width stays stable.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 3 hafta önce
ebeveyn
işleme
9503196329

+ 34 - 4
App for Indeed/Views/ProfilesListPageView.swift

@@ -31,7 +31,7 @@ final class ProfilesListPageView: NSView {
31
     private let documentView = ProfilesListDocumentView()
31
     private let documentView = ProfilesListDocumentView()
32
     private let contentStack = NSStackView()
32
     private let contentStack = NSStackView()
33
     private let emptyStateLabel = NSTextField(wrappingLabelWithString: "")
33
     private let emptyStateLabel = NSTextField(wrappingLabelWithString: "")
34
-    private let addButton = ProfilesPrimaryButton(title: "Add new profile  →", target: nil, action: nil)
34
+    private let addButton = ProfilesPrimaryButton(title: "Add new profile", showsTrailingArrow: true, target: nil, action: nil)
35
 
35
 
36
     override var userInterfaceLayoutDirection: NSUserInterfaceLayoutDirection {
36
     override var userInterfaceLayoutDirection: NSUserInterfaceLayoutDirection {
37
         get { .leftToRight }
37
         get { .leftToRight }
@@ -89,6 +89,9 @@ final class ProfilesListPageView: NSView {
89
         addButton.target = self
89
         addButton.target = self
90
         addButton.action = #selector(didTapAdd)
90
         addButton.action = #selector(didTapAdd)
91
         addButton.translatesAutoresizingMaskIntoConstraints = false
91
         addButton.translatesAutoresizingMaskIntoConstraints = false
92
+        // Keep the pill at intrinsic width so title + arrow stay grouped and centered; otherwise a wide stack pins text and icon to opposite edges.
93
+        addButton.setContentHuggingPriority(.required, for: .horizontal)
94
+        addButton.setContentCompressionResistancePriority(.required, for: .horizontal)
92
 
95
 
93
         let titleSubtitleStack = NSStackView(views: [title, subtitle])
96
         let titleSubtitleStack = NSStackView(views: [title, subtitle])
94
         titleSubtitleStack.orientation = .vertical
97
         titleSubtitleStack.orientation = .vertical
@@ -253,6 +256,9 @@ private final class ProfileListRowView: NSView {
253
 // MARK: - Primary CTA (matches profile page button)
256
 // MARK: - Primary CTA (matches profile page button)
254
 
257
 
255
 private final class ProfilesPrimaryButton: NSButton {
258
 private final class ProfilesPrimaryButton: NSButton {
259
+    /// Horizontal padding around title + icon. Extra trailing inset keeps the SF arrow off the capsule curve (`masksToBounds` can clip at the ends).
260
+    private static let intrinsicPadding = NSEdgeInsets(top: 12, left: 28, bottom: 12, right: 44)
261
+
256
     private var trackingArea: NSTrackingArea?
262
     private var trackingArea: NSTrackingArea?
257
     private var didPushCursor = false
263
     private var didPushCursor = false
258
 
264
 
@@ -266,26 +272,50 @@ private final class ProfilesPrimaryButton: NSButton {
266
         commonInit()
272
         commonInit()
267
     }
273
     }
268
 
274
 
269
-    convenience init(title: String, target: AnyObject?, action: Selector?) {
275
+    convenience init(title: String, showsTrailingArrow: Bool = false, target: AnyObject?, action: Selector?) {
270
         self.init(frame: .zero)
276
         self.init(frame: .zero)
271
         self.title = title
277
         self.title = title
272
         self.target = target
278
         self.target = target
273
         self.action = action
279
         self.action = action
280
+        if showsTrailingArrow {
281
+            imagePosition = .imageTrailing
282
+            let symbolConfig = NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold)
283
+            image = NSImage(systemSymbolName: "arrow.right", accessibilityDescription: nil)?
284
+                .withSymbolConfiguration(symbolConfig)
285
+        }
274
     }
286
     }
275
 
287
 
276
     private func commonInit() {
288
     private func commonInit() {
277
         bezelStyle = .rounded
289
         bezelStyle = .rounded
278
         isBordered = false
290
         isBordered = false
279
-        font = .systemFont(ofSize: 16, weight: .semibold)
291
+        font = .systemFont(ofSize: 15, weight: .semibold)
280
         contentTintColor = .white
292
         contentTintColor = .white
281
         wantsLayer = true
293
         wantsLayer = true
282
-        layer?.cornerRadius = 14
294
+        layer?.masksToBounds = true
283
         if #available(macOS 11.0, *) {
295
         if #available(macOS 11.0, *) {
284
             layer?.cornerCurve = .continuous
296
             layer?.cornerCurve = .continuous
285
         }
297
         }
286
         layer?.backgroundColor = ProfilesListPalette.brandBlue.cgColor
298
         layer?.backgroundColor = ProfilesListPalette.brandBlue.cgColor
287
     }
299
     }
288
 
300
 
301
+    override var intrinsicContentSize: NSSize {
302
+        let base = super.intrinsicContentSize
303
+        let p = Self.intrinsicPadding
304
+        // `NSButton` intrinsic width can sit a few points tight vs. the symbol’s painted bounds; add slack so the arrow never kisses the pill edge.
305
+        let trailingSlack: CGFloat = image != nil ? 10 : 0
306
+        return NSSize(
307
+            width: base.width + p.left + p.right + trailingSlack,
308
+            height: base.height + p.top + p.bottom
309
+        )
310
+    }
311
+
312
+    override func layout() {
313
+        super.layout()
314
+        let h = bounds.height
315
+        guard h > 1 else { return }
316
+        layer?.cornerRadius = h / 2
317
+    }
318
+
289
     override func updateTrackingAreas() {
319
     override func updateTrackingAreas() {
290
         super.updateTrackingAreas()
320
         super.updateTrackingAreas()
291
         if let trackingArea { removeTrackingArea(trackingArea) }
321
         if let trackingArea { removeTrackingArea(trackingArea) }