Kaynağa Gözat

Clean up repo and improve dashboard sidebar

Add a .gitignore for Xcode and SwiftPM build output so local DerivedData and similar paths stay out of version control. Remove the stray app_for_indeed directory that duplicated the root README and embedded a nested repository.

Update DashboardView so sidebar navigation keeps a selected index across renders, uses a tappable row host for the full pill hit target, and exposes clearer accessibility roles for each item.
AhtashamShahzad1 3 hafta önce
ebeveyn
işleme
87da1140f1
3 değiştirilmiş dosya ile 100 ekleme ve 11 silme
  1. 11 0
      .gitignore
  2. 89 10
      App for Indeed/Views/DashboardView.swift
  3. 0 1
      app_for_indeed

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
1
+# Xcode / Swift
2
+.DS_Store
3
+DerivedData/
4
+.derivedData/
5
+build/
6
+*.xcuserstate
7
+xcuserdata/
8
+
9
+# SwiftPM
10
+.build/
11
+.swiftpm/

+ 89 - 10
App for Indeed/Views/DashboardView.swift

@@ -37,6 +37,9 @@ final class DashboardView: NSView {
37
     private let searchField = NSTextField()
37
     private let searchField = NSTextField()
38
     private let scrollView = NSScrollView()
38
     private let scrollView = NSScrollView()
39
 
39
 
40
+    private var currentSidebarItems: [SidebarItem] = []
41
+    private var selectedSidebarIndex: Int = 0
42
+
40
     override init(frame frameRect: NSRect) {
43
     override init(frame frameRect: NSRect) {
41
         super.init(frame: frameRect)
44
         super.init(frame: frameRect)
42
         setupLayout()
45
         setupLayout()
@@ -55,7 +58,11 @@ final class DashboardView: NSView {
55
     func render(_ data: DashboardData) {
58
     func render(_ data: DashboardData) {
56
         greetingLabel.stringValue = "Welcome"
59
         greetingLabel.stringValue = "Welcome"
57
         subtitleLabel.stringValue = data.subtitle
60
         subtitleLabel.stringValue = data.subtitle
58
-        configureSidebar(data.sidebarItems)
61
+        currentSidebarItems = data.sidebarItems
62
+        if selectedSidebarIndex >= currentSidebarItems.count {
63
+            selectedSidebarIndex = max(0, currentSidebarItems.count - 1)
64
+        }
65
+        configureSidebar()
59
         updateDocumentLayout()
66
         updateDocumentLayout()
60
     }
67
     }
61
 
68
 
@@ -236,7 +243,8 @@ final class DashboardView: NSView {
236
         // Hook up search submission here when wiring up real data.
243
         // Hook up search submission here when wiring up real data.
237
     }
244
     }
238
 
245
 
239
-    private func configureSidebar(_ items: [SidebarItem]) {
246
+    private func configureSidebar() {
247
+        let items = currentSidebarItems
240
         sidebar.arrangedSubviews.forEach {
248
         sidebar.arrangedSubviews.forEach {
241
             sidebar.removeArrangedSubview($0)
249
             sidebar.removeArrangedSubview($0)
242
             $0.removeFromSuperview()
250
             $0.removeFromSuperview()
@@ -254,17 +262,26 @@ final class DashboardView: NSView {
254
         sidebar.addArrangedSubview(titleToMenuSpacer)
262
         sidebar.addArrangedSubview(titleToMenuSpacer)
255
 
263
 
256
         items.enumerated().forEach { index, item in
264
         items.enumerated().forEach { index, item in
265
+            let isSelected = index == selectedSidebarIndex
266
+
267
+            let rowHost = SidebarNavRowView { [weak self] in
268
+                self?.selectSidebarItem(at: index)
269
+            }
270
+            rowHost.translatesAutoresizingMaskIntoConstraints = false
271
+            rowHost.wantsLayer = true
272
+            rowHost.layer?.cornerRadius = 8
273
+            if isSelected {
274
+                rowHost.layer?.backgroundColor = Theme.selectionFill.cgColor
275
+            }
276
+            rowHost.setAccessibilityLabel(item.title)
277
+            rowHost.setAccessibilityRole(.button)
278
+            rowHost.setAccessibilitySelected(isSelected)
279
+
257
             let row = NSStackView()
280
             let row = NSStackView()
258
             row.orientation = .horizontal
281
             row.orientation = .horizontal
259
             row.spacing = 8
282
             row.spacing = 8
260
             row.alignment = .centerY
283
             row.alignment = .centerY
261
-            row.wantsLayer = true
262
-            row.layer?.cornerRadius = 8
263
-            row.edgeInsets = NSEdgeInsets(top: 8, left: 10, bottom: 8, right: 10)
264
-            let isSelected = index == 0
265
-            if isSelected {
266
-                row.layer?.backgroundColor = Theme.selectionFill.cgColor
267
-            }
284
+            row.translatesAutoresizingMaskIntoConstraints = false
268
 
285
 
269
             let icon = NSImageView()
286
             let icon = NSImageView()
270
             icon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 13, weight: .medium)
287
             icon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 13, weight: .medium)
@@ -274,6 +291,7 @@ final class DashboardView: NSView {
274
             let text = NSTextField(labelWithString: item.title)
291
             let text = NSTextField(labelWithString: item.title)
275
             text.font = .systemFont(ofSize: 14, weight: .medium)
292
             text.font = .systemFont(ofSize: 14, weight: .medium)
276
             text.textColor = isSelected ? Theme.primaryText : Theme.secondaryText
293
             text.textColor = isSelected ? Theme.primaryText : Theme.secondaryText
294
+            text.refusesFirstResponder = true
277
 
295
 
278
             row.addArrangedSubview(icon)
296
             row.addArrangedSubview(icon)
279
             row.addArrangedSubview(text)
297
             row.addArrangedSubview(text)
@@ -288,12 +306,21 @@ final class DashboardView: NSView {
288
                 badgeField.alignment = .center
306
                 badgeField.alignment = .center
289
                 badgeField.maximumNumberOfLines = 1
307
                 badgeField.maximumNumberOfLines = 1
290
                 badgeField.lineBreakMode = .byClipping
308
                 badgeField.lineBreakMode = .byClipping
309
+                badgeField.refusesFirstResponder = true
291
                 badgeField.translatesAutoresizingMaskIntoConstraints = false
310
                 badgeField.translatesAutoresizingMaskIntoConstraints = false
292
                 badgeField.widthAnchor.constraint(equalToConstant: 42).isActive = true
311
                 badgeField.widthAnchor.constraint(equalToConstant: 42).isActive = true
293
                 row.addArrangedSubview(NSView())
312
                 row.addArrangedSubview(NSView())
294
                 row.addArrangedSubview(badgeField)
313
                 row.addArrangedSubview(badgeField)
295
             }
314
             }
296
-            sidebar.addArrangedSubview(row)
315
+
316
+            rowHost.addSubview(row)
317
+            NSLayoutConstraint.activate([
318
+                row.leadingAnchor.constraint(equalTo: rowHost.leadingAnchor, constant: 10),
319
+                row.trailingAnchor.constraint(equalTo: rowHost.trailingAnchor, constant: -10),
320
+                row.topAnchor.constraint(equalTo: rowHost.topAnchor, constant: 8),
321
+                row.bottomAnchor.constraint(equalTo: rowHost.bottomAnchor, constant: -8)
322
+            ])
323
+            sidebar.addArrangedSubview(rowHost)
297
         }
324
         }
298
 
325
 
299
         let sidebarBottomSpacer = NSView()
326
         let sidebarBottomSpacer = NSView()
@@ -392,9 +419,61 @@ final class DashboardView: NSView {
392
         NSWorkspace.shared.open(url)
419
         NSWorkspace.shared.open(url)
393
     }
420
     }
394
 
421
 
422
+    private func selectSidebarItem(at index: Int) {
423
+        guard index >= 0, index < currentSidebarItems.count, index != selectedSidebarIndex else { return }
424
+        selectedSidebarIndex = index
425
+        configureSidebar()
426
+    }
427
+
395
     private func updateDocumentLayout() {
428
     private func updateDocumentLayout() {
396
         documentContainer.layoutSubtreeIfNeeded()
429
         documentContainer.layoutSubtreeIfNeeded()
397
         let fittingHeight = max(chromeContainer.fittingSize.height, bounds.height)
430
         let fittingHeight = max(chromeContainer.fittingSize.height, bounds.height)
398
         documentContainer.frame = NSRect(x: 0, y: 0, width: bounds.width, height: fittingHeight)
431
         documentContainer.frame = NSRect(x: 0, y: 0, width: bounds.width, height: fittingHeight)
399
     }
432
     }
400
 }
433
 }
434
+
435
+/// Captures clicks for the full sidebar pill so icon, label, and padding behave as one tab.
436
+private final class SidebarNavRowView: NSView {
437
+    private let onSelect: () -> Void
438
+
439
+    init(onSelect: @escaping () -> Void) {
440
+        self.onSelect = onSelect
441
+        super.init(frame: .zero)
442
+    }
443
+
444
+    @available(*, unavailable)
445
+    required init?(coder: NSCoder) {
446
+        fatalError("init(coder:) has not been implemented")
447
+    }
448
+
449
+    override func hitTest(_ point: NSPoint) -> NSView? {
450
+        guard let superview else { return super.hitTest(point) }
451
+        let local = convert(point, from: superview)
452
+        return bounds.contains(local) ? self : nil
453
+    }
454
+
455
+    override func mouseDown(with event: NSEvent) {
456
+        onSelect()
457
+    }
458
+
459
+    override func updateTrackingAreas() {
460
+        super.updateTrackingAreas()
461
+        trackingAreas.forEach { removeTrackingArea($0) }
462
+        addTrackingArea(NSTrackingArea(
463
+            rect: bounds,
464
+            options: [.activeInKeyWindow, .mouseEnteredAndExited, .inVisibleRect],
465
+            owner: self,
466
+            userInfo: nil
467
+        ))
468
+    }
469
+
470
+    override func mouseEntered(with event: NSEvent) {
471
+        super.mouseEntered(with: event)
472
+        NSCursor.pointingHand.push()
473
+    }
474
+
475
+    override func mouseExited(with event: NSEvent) {
476
+        super.mouseExited(with: event)
477
+        NSCursor.pop()
478
+    }
479
+}

+ 0 - 1
app_for_indeed

@@ -1 +0,0 @@
1
-Subproject commit 739c40a324e7bf07f4df03c69d916dd8a37475b6