Skip to content

Commit 632b549

Browse files
committed
Fix settings sheet layout
1 parent c718eaa commit 632b549

2 files changed

Lines changed: 100 additions & 45 deletions

File tree

Sources/Sub2APIStatusBar/MonitorPanel.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ struct MonitorPanel: View {
6969
.frame(width: 520, height: 680)
7070
.sheet(isPresented: $showingSettings) {
7171
SettingsView(model: model)
72-
.frame(width: 450, height: 690)
7372
}
7473
}
7574

Sources/Sub2APIStatusBar/SettingsView.swift

Lines changed: 100 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,56 +6,92 @@ struct SettingsView: View {
66
@Environment(\.dismiss) private var dismiss
77

88
var body: some View {
9-
VStack(alignment: .leading, spacing: 16) {
10-
Text("Settings")
11-
.font(.title2.bold())
9+
VStack(spacing: 0) {
10+
header
1211

1312
ScrollView {
1413
VStack(alignment: .leading, spacing: 16) {
15-
AccountSettingsSection(model: model)
14+
if !model.config.accounts.isEmpty {
15+
AccountSettingsSection(model: model)
16+
}
1617
GeneralSettingsSection(model: model)
1718
AlertSettingsSection(model: model)
1819
InsightSettingsSection(model: model)
1920
UpdateSettingsSection(model: model)
2021
LoginSettingsSection(model: model, dismiss: dismiss)
2122
DiagnosticsSettingsSection(model: model)
2223
}
24+
.padding(20)
25+
.frame(maxWidth: .infinity, alignment: .leading)
2326
}
27+
.frame(maxWidth: .infinity, maxHeight: .infinity)
2428

2529
if let error = model.settingsError {
2630
Text(error)
2731
.font(.caption)
2832
.foregroundStyle(.red)
2933
.lineLimit(3)
34+
.frame(maxWidth: .infinity, alignment: .leading)
35+
.padding(.horizontal, 20)
36+
.padding(.vertical, 10)
37+
.background(Color.red.opacity(0.08))
3038
}
3139

40+
Divider()
41+
footer
42+
}
43+
.frame(width: 520, height: 680)
44+
.background(PanelBackground())
45+
}
46+
47+
private var header: some View {
48+
HStack(spacing: 12) {
49+
Image(systemName: "gearshape.fill")
50+
.font(.system(size: 18, weight: .semibold))
51+
.foregroundStyle(.blue)
52+
.frame(width: 34, height: 34)
53+
.background(Color.blue.opacity(0.13), in: RoundedRectangle(cornerRadius: 8))
54+
55+
Text("Settings")
56+
.font(.title2.bold())
57+
3258
Spacer()
59+
}
60+
.padding(.horizontal, 20)
61+
.padding(.vertical, 16)
62+
.background(PanelColors.elevatedSurface.opacity(0.88))
63+
.overlay(alignment: .bottom) {
64+
Rectangle()
65+
.fill(PanelColors.border)
66+
.frame(height: 1)
67+
}
68+
}
3369

34-
HStack {
35-
Spacer()
36-
Button("Cancel") {
37-
dismiss()
38-
}
39-
Button("Save") {
40-
model.saveSettings()
41-
dismiss()
42-
}
43-
.buttonStyle(.borderedProminent)
70+
private var footer: some View {
71+
HStack {
72+
Spacer()
73+
Button("Cancel") {
74+
dismiss()
75+
}
76+
Button("Save") {
77+
model.saveSettings()
78+
dismiss()
4479
}
80+
.buttonStyle(.borderedProminent)
4581
}
46-
.padding(20)
82+
.padding(.horizontal, 20)
83+
.padding(.vertical, 14)
84+
.background(PanelColors.elevatedSurface.opacity(0.92))
4785
}
4886
}
4987

5088
struct AccountSettingsSection: View {
5189
@ObservedObject var model: MonitorViewModel
5290

5391
var body: some View {
54-
if !model.config.accounts.isEmpty {
55-
VStack(alignment: .leading, spacing: 8) {
56-
Text("Accounts")
57-
.font(.headline)
58-
Picker("Active Account", selection: Binding(
92+
SettingsSection(title: "Accounts") {
93+
SettingsControlRow(title: "Active Account") {
94+
Picker("", selection: Binding(
5995
get: { model.config.selectedAccountID ?? "" },
6096
set: { id in
6197
if let account = model.config.accounts.first(where: { $0.id == id }) {
@@ -68,6 +104,8 @@ struct AccountSettingsSection: View {
68104
Text(account.displayName).tag(account.id)
69105
}
70106
}
107+
.labelsHidden()
108+
.frame(maxWidth: .infinity, alignment: .leading)
71109
}
72110
}
73111
}
@@ -77,14 +115,12 @@ struct GeneralSettingsSection: View {
77115
@ObservedObject var model: MonitorViewModel
78116

79117
var body: some View {
80-
VStack(alignment: .leading, spacing: 8) {
81-
Text("General")
82-
.font(.headline)
83-
118+
SettingsSection(title: "General") {
84119
VStack(spacing: 8) {
85120
SettingsControlRow(title: "Base URL") {
86121
TextField("https://sub2api.example.com", text: $model.settingsDraft.baseURL)
87122
.textFieldStyle(.roundedBorder)
123+
.frame(maxWidth: .infinity)
88124
}
89125

90126
SettingsControlRow(title: "Menu Bar") {
@@ -98,7 +134,9 @@ struct GeneralSettingsSection: View {
98134
}
99135
}
100136
.pickerStyle(.segmented)
137+
.controlSize(.small)
101138
.disabled(!model.settingsDraft.showsMenuBarText)
139+
.frame(maxWidth: .infinity)
102140
}
103141

104142
SettingsControlRow(title: "Startup") {
@@ -110,6 +148,7 @@ struct GeneralSettingsSection: View {
110148

111149
SettingsControlRow(title: "Refresh") {
112150
Slider(value: $model.settingsDraft.refreshIntervalSeconds, in: 5...300, step: 5)
151+
.frame(maxWidth: .infinity)
113152
Text("\(Int(model.settingsDraft.refreshIntervalSeconds))s")
114153
.font(.callout.monospacedDigit())
115154
.frame(width: 42, alignment: .trailing)
@@ -118,6 +157,7 @@ struct GeneralSettingsSection: View {
118157
SettingsControlRow(title: "Token") {
119158
SecureField("Bearer Token", text: $model.settingsDraft.authToken)
120159
.textFieldStyle(.roundedBorder)
160+
.frame(maxWidth: .infinity)
121161
}
122162
}
123163

@@ -132,10 +172,7 @@ struct AlertSettingsSection: View {
132172
@ObservedObject var model: MonitorViewModel
133173

134174
var body: some View {
135-
VStack(alignment: .leading, spacing: 8) {
136-
Text("Alerts")
137-
.font(.headline)
138-
175+
SettingsSection(title: "Alerts") {
139176
VStack(spacing: 8) {
140177
SettingsControlRow(title: "Notify") {
141178
Toggle("Usage insights", isOn: $model.settingsDraft.insightAlertSettings.isEnabled)
@@ -147,10 +184,13 @@ struct AlertSettingsSection: View {
147184
Text("Error only").tag(MonitorSeverity.error)
148185
}
149186
.pickerStyle(.segmented)
187+
.controlSize(.small)
188+
.frame(maxWidth: 320, alignment: .leading)
150189
}
151190

152191
SettingsControlRow(title: "Quiet") {
153192
Slider(value: $model.settingsDraft.insightAlertSettings.cooldownMinutes, in: 5...360, step: 5)
193+
.frame(maxWidth: .infinity)
154194
Text("\(Int(model.settingsDraft.insightAlertSettings.cooldownMinutes))m")
155195
.font(.callout.monospacedDigit())
156196
.frame(width: 48, alignment: .trailing)
@@ -166,10 +206,7 @@ struct InsightSettingsSection: View {
166206
@ObservedObject var model: MonitorViewModel
167207

168208
var body: some View {
169-
VStack(alignment: .leading, spacing: 8) {
170-
Text("Insights")
171-
.font(.headline)
172-
209+
SettingsSection(title: "Insights") {
173210
VStack(spacing: 8) {
174211
ThresholdSliderRow(
175212
title: "Quota warn",
@@ -202,6 +239,7 @@ struct InsightSettingsSection: View {
202239
format: .number.precision(.fractionLength(0...2))
203240
)
204241
.textFieldStyle(.roundedBorder)
242+
.frame(maxWidth: .infinity)
205243
Text("USD")
206244
.font(.callout)
207245
.foregroundStyle(.secondary)
@@ -341,6 +379,7 @@ struct ThresholdSliderRow: View {
341379
var body: some View {
342380
SettingsControlRow(title: title) {
343381
Slider(value: $value, in: range, step: step)
382+
.frame(maxWidth: .infinity)
344383
Text(valueText)
345384
.font(.callout.monospacedDigit())
346385
.frame(width: 48, alignment: .trailing)
@@ -353,15 +392,36 @@ struct SettingsControlRow<Content: View>: View {
353392
@ViewBuilder let content: Content
354393

355394
var body: some View {
356-
HStack(alignment: .center, spacing: 10) {
395+
VStack(alignment: .leading, spacing: 6) {
357396
Text(title)
358-
.font(.callout)
397+
.font(.caption.weight(.medium))
359398
.foregroundStyle(.secondary)
360-
.frame(width: 76, alignment: .leading)
361399
HStack(spacing: 8) {
362400
content
363401
}
402+
.frame(maxWidth: .infinity, alignment: .leading)
364403
}
404+
.frame(maxWidth: .infinity, alignment: .leading)
405+
}
406+
}
407+
408+
struct SettingsSection<Content: View>: View {
409+
let title: String
410+
@ViewBuilder let content: Content
411+
412+
var body: some View {
413+
VStack(alignment: .leading, spacing: 10) {
414+
Text(title)
415+
.font(.headline)
416+
content
417+
}
418+
.padding(14)
419+
.frame(maxWidth: .infinity, alignment: .leading)
420+
.background(PanelColors.elevatedSurface.opacity(0.88), in: RoundedRectangle(cornerRadius: 8))
421+
.overlay(
422+
RoundedRectangle(cornerRadius: 8)
423+
.stroke(PanelColors.border, lineWidth: 1)
424+
)
365425
}
366426
}
367427

@@ -370,11 +430,11 @@ struct LoginSettingsSection: View {
370430
let dismiss: DismissAction
371431

372432
var body: some View {
373-
VStack(alignment: .leading, spacing: 8) {
374-
Text("Login")
375-
.font(.headline)
433+
SettingsSection(title: "Login") {
376434
TextField("Email", text: $model.loginEmail)
435+
.textFieldStyle(.roundedBorder)
377436
SecureField("Password", text: $model.loginPassword)
437+
.textFieldStyle(.roundedBorder)
378438
Button {
379439
model.loginAndSave()
380440
} label: {
@@ -397,9 +457,7 @@ struct DiagnosticsSettingsSection: View {
397457
@ObservedObject var model: MonitorViewModel
398458

399459
var body: some View {
400-
VStack(alignment: .leading, spacing: 8) {
401-
Text("Diagnostics")
402-
.font(.headline)
460+
SettingsSection(title: "Diagnostics") {
403461
VStack(alignment: .leading, spacing: 8) {
404462
HStack {
405463
Button {
@@ -440,10 +498,8 @@ struct UpdateSettingsSection: View {
440498
@ObservedObject var model: MonitorViewModel
441499

442500
var body: some View {
443-
VStack(alignment: .leading, spacing: 8) {
501+
SettingsSection(title: "Updates") {
444502
HStack {
445-
Text("Updates")
446-
.font(.headline)
447503
Spacer()
448504
if model.isCheckingForUpdates {
449505
ProgressView()

0 commit comments

Comments
 (0)