|
|
@@ -32,12 +32,9 @@ final class DashboardView: NSView {
|
|
32
|
32
|
private let mainOverlay = NSStackView()
|
|
33
|
33
|
private let greetingLabel = NSTextField(labelWithString: "")
|
|
34
|
34
|
private let subtitleLabel = NSTextField(labelWithString: "")
|
|
35
|
|
- private let insightsCard = NSView()
|
|
36
|
|
- private let insightsTitleLabel = NSTextField(labelWithString: "")
|
|
37
|
|
- private let insightsBodyLabel = NSTextField(labelWithString: "")
|
|
38
|
|
- private let togglesLabel = NSTextField(labelWithString: "Show:")
|
|
39
|
|
- private let savedToggleButton = NSButton(title: "Saved", target: nil, action: nil)
|
|
40
|
|
- private let interviewsToggleButton = NSButton(title: "Interviews", target: nil, action: nil)
|
|
|
35
|
+ private let searchCard = NSView()
|
|
|
36
|
+ private let searchIcon = NSImageView()
|
|
|
37
|
+ private let searchField = NSTextField()
|
|
41
|
38
|
private let sparkleView = NSImageView()
|
|
42
|
39
|
private let scrollView = NSScrollView()
|
|
43
|
40
|
|
|
|
@@ -59,8 +56,6 @@ final class DashboardView: NSView {
|
|
59
|
56
|
func render(_ data: DashboardData) {
|
|
60
|
57
|
greetingLabel.stringValue = "Welcome"
|
|
61
|
58
|
subtitleLabel.stringValue = data.subtitle
|
|
62
|
|
- insightsTitleLabel.stringValue = data.profileInsightsTitle
|
|
63
|
|
- insightsBodyLabel.stringValue = data.profileInsightsBody
|
|
64
|
59
|
configureSidebar(data.sidebarItems)
|
|
65
|
60
|
updateDocumentLayout()
|
|
66
|
61
|
}
|
|
|
@@ -130,7 +125,7 @@ final class DashboardView: NSView {
|
|
130
|
125
|
topInset.translatesAutoresizingMaskIntoConstraints = false
|
|
131
|
126
|
topInset.heightAnchor.constraint(equalToConstant: 32).isActive = true
|
|
132
|
127
|
|
|
133
|
|
- configureInsightsCard()
|
|
|
128
|
+ configureSearchCard()
|
|
134
|
129
|
|
|
135
|
130
|
let titleBlock = NSStackView(views: [greetingLabel, subtitleLabel])
|
|
136
|
131
|
titleBlock.orientation = .vertical
|
|
|
@@ -149,7 +144,7 @@ final class DashboardView: NSView {
|
|
149
|
144
|
mainOverlay.addArrangedSubview(topInset)
|
|
150
|
145
|
mainOverlay.addArrangedSubview(titleBlock)
|
|
151
|
146
|
mainOverlay.addArrangedSubview(midSpacer)
|
|
152
|
|
- mainOverlay.addArrangedSubview(insightsCard)
|
|
|
147
|
+ mainOverlay.addArrangedSubview(searchCard)
|
|
153
|
148
|
mainOverlay.addArrangedSubview(overlayBottomSpacer)
|
|
154
|
149
|
|
|
155
|
150
|
sparkleView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -185,6 +180,8 @@ final class DashboardView: NSView {
|
|
185
|
180
|
mainOverlay.topAnchor.constraint(equalTo: mainHost.topAnchor),
|
|
186
|
181
|
mainOverlay.bottomAnchor.constraint(equalTo: mainHost.bottomAnchor, constant: -24),
|
|
187
|
182
|
|
|
|
183
|
+ searchCard.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.82),
|
|
|
184
|
+
|
|
188
|
185
|
greetingLabel.leadingAnchor.constraint(equalTo: mainOverlay.leadingAnchor, constant: 24),
|
|
189
|
186
|
greetingLabel.trailingAnchor.constraint(equalTo: mainOverlay.trailingAnchor, constant: -24),
|
|
190
|
187
|
subtitleLabel.leadingAnchor.constraint(equalTo: greetingLabel.leadingAnchor),
|
|
|
@@ -195,70 +192,58 @@ final class DashboardView: NSView {
|
|
195
|
192
|
])
|
|
196
|
193
|
}
|
|
197
|
194
|
|
|
198
|
|
- private func configureInsightsCard() {
|
|
199
|
|
- insightsCard.wantsLayer = true
|
|
200
|
|
- insightsCard.layer?.backgroundColor = Theme.cardBackground.cgColor
|
|
201
|
|
- insightsCard.layer?.cornerRadius = 18
|
|
202
|
|
- insightsCard.translatesAutoresizingMaskIntoConstraints = false
|
|
203
|
|
-
|
|
204
|
|
- insightsTitleLabel.font = .systemFont(ofSize: 20, weight: .semibold)
|
|
205
|
|
- insightsTitleLabel.textColor = Theme.primaryText
|
|
206
|
|
- insightsTitleLabel.alignment = .center
|
|
207
|
|
- insightsTitleLabel.maximumNumberOfLines = 1
|
|
208
|
|
-
|
|
209
|
|
- insightsBodyLabel.font = .systemFont(ofSize: 13, weight: .regular)
|
|
210
|
|
- insightsBodyLabel.textColor = Theme.secondaryText
|
|
211
|
|
- insightsBodyLabel.alignment = .center
|
|
212
|
|
- insightsBodyLabel.maximumNumberOfLines = 4
|
|
213
|
|
- insightsBodyLabel.lineBreakMode = .byWordWrapping
|
|
214
|
|
- insightsBodyLabel.preferredMaxLayoutWidth = 400
|
|
215
|
|
-
|
|
216
|
|
- togglesLabel.font = .systemFont(ofSize: 12, weight: .medium)
|
|
217
|
|
- togglesLabel.textColor = Theme.tertiaryText
|
|
218
|
|
- togglesLabel.alignment = .center
|
|
219
|
|
- togglesLabel.maximumNumberOfLines = 1
|
|
220
|
|
-
|
|
221
|
|
- styleToggle(savedToggleButton)
|
|
222
|
|
- styleToggle(interviewsToggleButton)
|
|
223
|
|
-
|
|
224
|
|
- let toggleRow = NSStackView(views: [savedToggleButton, interviewsToggleButton])
|
|
225
|
|
- toggleRow.orientation = .horizontal
|
|
226
|
|
- toggleRow.spacing = 10
|
|
227
|
|
- toggleRow.alignment = .centerY
|
|
228
|
|
-
|
|
229
|
|
- let inner = NSStackView(views: [
|
|
230
|
|
- insightsTitleLabel,
|
|
231
|
|
- insightsBodyLabel,
|
|
232
|
|
- togglesLabel,
|
|
233
|
|
- toggleRow
|
|
234
|
|
- ])
|
|
235
|
|
- inner.orientation = .vertical
|
|
236
|
|
- inner.spacing = 10
|
|
237
|
|
- inner.alignment = .centerX
|
|
238
|
|
- inner.distribution = .fill
|
|
239
|
|
- inner.translatesAutoresizingMaskIntoConstraints = false
|
|
|
195
|
+ private func configureSearchCard() {
|
|
|
196
|
+ searchCard.wantsLayer = true
|
|
|
197
|
+ searchCard.layer?.backgroundColor = Theme.cardBackground.cgColor
|
|
|
198
|
+ searchCard.layer?.cornerRadius = 14
|
|
|
199
|
+ searchCard.layer?.borderWidth = 1
|
|
|
200
|
+ searchCard.layer?.borderColor = NSColor(calibratedWhite: 1, alpha: 0.06).cgColor
|
|
|
201
|
+ searchCard.translatesAutoresizingMaskIntoConstraints = false
|
|
|
202
|
+ searchCard.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
|
|
203
|
+
|
|
|
204
|
+ searchIcon.translatesAutoresizingMaskIntoConstraints = false
|
|
|
205
|
+ searchIcon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 15, weight: .medium)
|
|
|
206
|
+ searchIcon.image = NSImage(systemSymbolName: "magnifyingglass", accessibilityDescription: "Search")
|
|
|
207
|
+ searchIcon.contentTintColor = Theme.secondaryText
|
|
|
208
|
+
|
|
|
209
|
+ searchField.translatesAutoresizingMaskIntoConstraints = false
|
|
|
210
|
+ searchField.isBordered = false
|
|
|
211
|
+ searchField.drawsBackground = false
|
|
|
212
|
+ searchField.focusRingType = .none
|
|
|
213
|
+ searchField.font = .systemFont(ofSize: 14, weight: .regular)
|
|
|
214
|
+ searchField.textColor = Theme.primaryText
|
|
|
215
|
+ searchField.placeholderAttributedString = NSAttributedString(
|
|
|
216
|
+ string: "Search jobs, companies, or locations",
|
|
|
217
|
+ attributes: [
|
|
|
218
|
+ .foregroundColor: Theme.tertiaryText,
|
|
|
219
|
+ .font: NSFont.systemFont(ofSize: 14, weight: .regular)
|
|
|
220
|
+ ]
|
|
|
221
|
+ )
|
|
|
222
|
+ searchField.cell?.usesSingleLineMode = true
|
|
|
223
|
+ searchField.cell?.wraps = false
|
|
|
224
|
+ searchField.cell?.isScrollable = true
|
|
|
225
|
+ searchField.target = self
|
|
|
226
|
+ searchField.action = #selector(didSubmitSearch)
|
|
|
227
|
+
|
|
|
228
|
+ searchCard.addSubview(searchIcon)
|
|
|
229
|
+ searchCard.addSubview(searchField)
|
|
240
|
230
|
|
|
241
|
|
- insightsCard.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
|
242
|
|
- insightsCard.addSubview(inner)
|
|
243
|
231
|
NSLayoutConstraint.activate([
|
|
244
|
|
- inner.leadingAnchor.constraint(equalTo: insightsCard.leadingAnchor, constant: 32),
|
|
245
|
|
- inner.trailingAnchor.constraint(equalTo: insightsCard.trailingAnchor, constant: -32),
|
|
246
|
|
- inner.topAnchor.constraint(equalTo: insightsCard.topAnchor, constant: 22),
|
|
247
|
|
- inner.bottomAnchor.constraint(equalTo: insightsCard.bottomAnchor, constant: -22),
|
|
248
|
|
- insightsCard.widthAnchor.constraint(equalToConstant: 440)
|
|
|
232
|
+ searchCard.heightAnchor.constraint(equalToConstant: 48),
|
|
|
233
|
+
|
|
|
234
|
+ searchIcon.leadingAnchor.constraint(equalTo: searchCard.leadingAnchor, constant: 16),
|
|
|
235
|
+ searchIcon.centerYAnchor.constraint(equalTo: searchCard.centerYAnchor),
|
|
|
236
|
+ searchIcon.widthAnchor.constraint(equalToConstant: 18),
|
|
|
237
|
+ searchIcon.heightAnchor.constraint(equalToConstant: 18),
|
|
|
238
|
+
|
|
|
239
|
+ searchField.leadingAnchor.constraint(equalTo: searchIcon.trailingAnchor, constant: 10),
|
|
|
240
|
+ searchField.trailingAnchor.constraint(equalTo: searchCard.trailingAnchor, constant: -16),
|
|
|
241
|
+ searchField.centerYAnchor.constraint(equalTo: searchCard.centerYAnchor)
|
|
249
|
242
|
])
|
|
250
|
243
|
}
|
|
251
|
244
|
|
|
252
|
|
- private func styleToggle(_ button: NSButton) {
|
|
253
|
|
- button.bezelStyle = .rounded
|
|
254
|
|
- button.font = .systemFont(ofSize: 12, weight: .medium)
|
|
255
|
|
- button.contentTintColor = Theme.secondaryText
|
|
256
|
|
- button.wantsLayer = true
|
|
257
|
|
- button.layer?.backgroundColor = Theme.toggleBackground.cgColor
|
|
258
|
|
- button.layer?.cornerRadius = 8
|
|
259
|
|
- button.translatesAutoresizingMaskIntoConstraints = false
|
|
260
|
|
- button.widthAnchor.constraint(equalToConstant: 108).isActive = true
|
|
261
|
|
- button.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
|
|
245
|
+ @objc private func didSubmitSearch() {
|
|
|
246
|
+ // Hook up search submission here when wiring up real data.
|
|
262
|
247
|
}
|
|
263
|
248
|
|
|
264
|
249
|
private func configureSidebar(_ items: [SidebarItem]) {
|