This PR contains the work done to implement the **List Tasks** feature, and to integrate it into the main content view of the app. Reviewed-on: rock-n-code/todos-app#4 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
149 lines
4.2 KiB
Swift
149 lines
4.2 KiB
Swift
import SwiftUI
|
|
|
|
extension TaskListView {
|
|
|
|
/// A view that displays a single editable todo task in a list.
|
|
///
|
|
/// This view provides a comprehensive interface for viewing and editing task information,
|
|
/// including:
|
|
/// - A completion toggle using a circular checkbox design
|
|
/// - An editable title field with multi-line support
|
|
/// - An optional note field that appears when the task has notes
|
|
/// - A date picker for selecting the task's due date
|
|
///
|
|
/// The view automatically adjusts its appearance based on completion state, dimming
|
|
/// completed tasks to provide visual feedback. All text fields support automatic
|
|
/// capitalization and keyboard correction for improved user experience.
|
|
struct Item: View {
|
|
|
|
// MARK: Properties
|
|
|
|
/// The todo task to display.
|
|
@Bindable
|
|
private var item: Todo
|
|
|
|
// MARK: Initializers
|
|
|
|
/// Creates a new task list item view for the specified todo.
|
|
///
|
|
/// The item is wrapped in a `@Bindable` property wrapper to enable two-way
|
|
/// data binding for editing task properties directly within the view.
|
|
///
|
|
/// - Parameter item: The todo task to display and edit.
|
|
init(_ item: Todo) {
|
|
_item = Bindable(item)
|
|
}
|
|
|
|
// MARK: Body
|
|
|
|
/// The content and behavior of the view.
|
|
///
|
|
/// Renders an editable task list item with a completion toggle, title field,
|
|
/// optional note field, and date picker. The view's foreground color automatically
|
|
/// adjusts based on completion state, and changes animate smoothly.
|
|
var body: some View {
|
|
HStack(
|
|
alignment: .top,
|
|
spacing: 16
|
|
) {
|
|
Toggle(
|
|
"view.task-list-item.placeholder.completed",
|
|
isOn: isCompleted
|
|
)
|
|
.toggleStyle(.completion)
|
|
|
|
VStack(
|
|
alignment: .leading,
|
|
spacing: 8
|
|
) {
|
|
TextField(
|
|
"view.task-list-item.placeholder.title",
|
|
text: $item.title,
|
|
axis: .vertical
|
|
)
|
|
.font(.headline)
|
|
|
|
if showNote {
|
|
TextField(
|
|
"view.task-list-item.placeholder.note",
|
|
text: note,
|
|
axis: .vertical
|
|
)
|
|
.font(.subheadline)
|
|
}
|
|
|
|
DatePicker(selection: $item.due, displayedComponents: .date) {
|
|
Image(systemName: "calendar")
|
|
}
|
|
.frame(width: 160)
|
|
.disabled(isCompleted.wrappedValue)
|
|
}
|
|
.autocapitalization(.sentences)
|
|
.autocorrectionDisabled(false)
|
|
.foregroundStyle(foregroundColor)
|
|
.keyboardType(.asciiCapable)
|
|
}
|
|
.animation(
|
|
.easeInOut,
|
|
value: item.completed
|
|
)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private extension TaskListView.Item {
|
|
|
|
// MARK: Bindings
|
|
|
|
var isCompleted: Binding<Bool> {
|
|
.init {
|
|
item.completed != nil
|
|
} set: {
|
|
item.completed = $0 ? .now : nil
|
|
}
|
|
}
|
|
|
|
var note: Binding<String> {
|
|
.init {
|
|
item.note ?? ""
|
|
} set: {
|
|
item.note = $0.isEmpty ? nil : $0
|
|
}
|
|
}
|
|
|
|
// MARK: Computed
|
|
|
|
var foregroundColor: Color {
|
|
item.completed == nil ? .primary : .secondary
|
|
}
|
|
|
|
var showNote: Bool {
|
|
guard let note = item.note else {
|
|
return false
|
|
}
|
|
|
|
return note.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Previews
|
|
|
|
#Preview("Task list item") {
|
|
@Previewable
|
|
var todo: Todo = .init(
|
|
title: "Complete project documentation",
|
|
note: "Add detailed comments and README",
|
|
due: .now,
|
|
completed: nil
|
|
)
|
|
|
|
List {
|
|
TaskListView.Item(todo)
|
|
}
|
|
.listStyle(.grouped)
|
|
}
|