Acord/src/SidebarView.swift

133 lines
4.9 KiB
Swift

import SwiftUI
struct SidebarView: View {
@ObservedObject var state: AppState
@State private var lastClickedID: UUID?
private let dateFormatter: DateFormatter = {
let f = DateFormatter()
f.dateStyle = .short
f.timeStyle = .short
return f
}()
var body: some View {
VStack(spacing: 0) {
HStack {
Text("Notes")
.font(.headline)
.foregroundColor(Color(ns: Theme.current.text))
Spacer()
Button(action: { state.newNote() }) {
Image(systemName: "plus")
.foregroundColor(Color(ns: Theme.current.text))
}
.buttonStyle(.plain)
.help("New Note")
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
Divider()
if state.noteList.isEmpty {
VStack {
Spacer()
Text("No notes yet")
.foregroundColor(Color(ns: Theme.current.overlay1))
.font(Font(Theme.sidebarFont as CTFont))
Spacer()
}
} else {
ScrollView {
LazyVStack(spacing: 0) {
ForEach(Array(state.noteList.enumerated()), id: \.element.id) { index, note in
NoteRow(
note: note,
isSelected: state.selectedNoteIDs.contains(note.id),
isActive: note.id == state.currentNoteID,
dateFormatter: dateFormatter
)
.contentShape(Rectangle())
.onTapGesture(count: 2) {
state.openNote(note.id)
}
.onTapGesture(count: 1) {
handleClick(note: note, index: index)
}
.contextMenu {
if state.selectedNoteIDs.count > 1 && state.selectedNoteIDs.contains(note.id) {
Button("Delete \(state.selectedNoteIDs.count) Notes") {
state.deleteNotes(state.selectedNoteIDs)
lastClickedID = nil
}
} else {
Button("Delete") { state.deleteNote(note.id) }
}
}
}
}
}
.onDeleteCommand {
guard !state.selectedNoteIDs.isEmpty else { return }
state.deleteNotes(state.selectedNoteIDs)
lastClickedID = nil
}
}
}
.background(Color(ns: Theme.current.base))
.onAppear { state.refreshNoteList() }
}
private func handleClick(note: NoteInfo, index: Int) {
let flags = NSEvent.modifierFlags
if flags.contains(.command) {
state.selectNote(note.id, extend: true)
lastClickedID = note.id
} else if flags.contains(.shift), let lastID = lastClickedID {
if let lastIndex = state.noteList.firstIndex(where: { $0.id == lastID }) {
let range = min(lastIndex, index)...max(lastIndex, index)
var ids = Set<UUID>()
for i in range {
ids.insert(state.noteList[i].id)
}
state.selectedNoteIDs = ids
}
} else {
state.selectNote(note.id)
lastClickedID = note.id
}
}
}
struct NoteRow: View {
let note: NoteInfo
let isSelected: Bool
var isActive: Bool = false
let dateFormatter: DateFormatter
var body: some View {
VStack(alignment: .leading, spacing: 2) {
Text(note.title)
.font(Font(isActive
? NSFontManager.shared.convert(Theme.sidebarFont, toHaveTrait: .boldFontMask) as CTFont
: Theme.sidebarFont as CTFont))
.foregroundColor(Color(ns: Theme.current.text))
.lineLimit(1)
Text(dateFormatter.string(from: note.lastModified))
.font(Font(Theme.sidebarDateFont as CTFont))
.foregroundColor(Color(ns: Theme.current.subtext0))
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 4)
.padding(.horizontal, 12)
.background(
isActive
? Color(ns: Theme.current.surface1)
: isSelected
? Color(ns: Theme.current.surface0)
: Color.clear
)
}
}