133 lines
4.9 KiB
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
|
|
)
|
|
}
|
|
}
|