List tasks feature #4

Merged
javier merged 6 commits from app/list-tasks into main 2026-02-10 15:31:53 +00:00
2 changed files with 181 additions and 0 deletions
Showing only changes of commit 1385f0df34 - Show all commits
+33
View File
@@ -121,6 +121,39 @@
}
}
}
"view.task-list-item.placeholder.completed" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Tap to complete or not your task"
}
}
}
},
"view.task-list-item.placeholder.note" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add extra notes to your task (if needed)"
}
}
}
},
"view.task-list-item.placeholder.title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Give a title to your task"
}
}
}
},
}
},
"version" : "1.1"
@@ -0,0 +1,148 @@
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)
}