Project arrow keys

This commit is contained in:
Ian Gulliver
2026-10-20 15:50:06 -07:00
parent d096d604c9
commit c0c4955ca2
2 changed files with 45 additions and 9 deletions
+44 -8
View File
@@ -11,20 +11,38 @@ struct ProjectView: View {
@Environment(Overlays.self) private var overlays
@ViewState private var todos: [String: Result<Todo, ApiError>] = [:]
@ViewState private var off: (() -> Void)?
@FocusState private var keys: Bool
var body: some View {
ScrollView {
if let p = store.focusedProject() {
ProjectBlock(project: p, todo: todos[p.id])
.padding(.horizontal, 16)
.padding(.top, 8)
.padding(.bottom, 16)
} else {
Muted(text: "No project selected.")
ScrollViewReader { proxy in
ScrollView {
if let p = store.focusedProject() {
ProjectBlock(project: p, todo: todos[p.id])
.padding(.horizontal, 16)
.padding(.top, 8)
.padding(.bottom, 16)
} else {
Muted(text: "No project selected.")
}
}
.onChange(of: store.selectedTaskId) {
if let id = store.selectedTaskId {
proxy.scrollTo(id)
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.focusable()
.focusEffectDisabled()
.focused($keys)
.onKeyPress(keys: [.upArrow, .downArrow]) { press in
step(press.key == .downArrow ? 1 : -1)
return .handled
}
.onAppear {
if !(overlays.window?.firstResponder is NSTextView) {
keys = true
}
off = store.events.add { ev in
if refreshKinds.contains(ev.kind), let projectId = ev.projectId {
load(projectId)
@@ -51,6 +69,23 @@ struct ProjectView: View {
}
}
private func step(_ by: Int) {
guard let p = store.focusedProject() else {
return
}
let rows = nested(store, (store.tasks[p.id] ?? []).filter { $0.status == .active }).map(\.task.id)
if rows.isEmpty {
return
}
let next: Int
if let i = rows.firstIndex(where: { $0 == store.selectedTaskId }) {
next = min(max(i + by, 0), rows.count - 1)
} else {
next = by > 0 ? 0 : rows.count - 1
}
store.focusTask(rows[next], .project)
}
private func refresh() {
if let p = store.focusedProject() {
load(p.id)
@@ -128,6 +163,7 @@ private struct TaskRows: View {
VStack(alignment: .leading, spacing: 0) {
ForEach(nested(store, active), id: \.task.id) { row in
TaskRow(task: row.task, depth: row.depth)
.id(row.task.id)
}
}
.padding(.bottom, 8)