Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public struct Tag: View {
.foregroundStyle(Color.accent)
.bold()
.lineLimit(1)
.fixedSize()
.padding(.vertical, 4)
.padding(.leading, 8)
.padding(.trailing, isEditing ? 0 : 8)
Expand Down Expand Up @@ -245,7 +244,7 @@ private struct TagLayout: Layout {
var minX = bounds.minX

for index in row.indices {
let size = subviews[index].sizeThatFits(.unspecified)
let size = fittedSize(of: subviews[index], maxWidth: width)
subviews[index].place(
at: CGPoint(x: minX, y: minY),
proposal: ProposedViewSize(size)
Expand Down Expand Up @@ -292,7 +291,7 @@ private struct TagLayout: Layout {

for index in contentIndices {
let subview = subviews[index]
let size = subview.sizeThatFits(.unspecified)
let size = fittedSize(of: subview, maxWidth: availableWidth)

if currentWidth + size.width > availableWidth && !currentRow.indices.isEmpty {
rows.append(currentRow)
Expand Down Expand Up @@ -330,7 +329,7 @@ private struct TagLayout: Layout {
}

let overflowIndex = subviews.count - 1
let overflowSize = subviews[overflowIndex].sizeThatFits(.unspecified)
let overflowSize = fittedSize(of: subviews[overflowIndex], maxWidth: maxWidth)
guard !limited.isEmpty else {
return [Row(indices: [overflowIndex], maxHeight: overflowSize.height, width: overflowSize.width)]
}
Expand All @@ -341,7 +340,7 @@ private struct TagLayout: Layout {

while !candidateIndices.isEmpty {
let rowIndices = candidateIndices + [overflowIndex]
let rowWidth = width(for: rowIndices, subviews: subviews)
let rowWidth = width(for: rowIndices, subviews: subviews, maxWidth: maxWidth)
if rowWidth <= maxWidth {
lastRow.indices = rowIndices
lastRow.maxHeight = max(lastRow.maxHeight, overflowSize.height)
Expand All @@ -360,15 +359,25 @@ private struct TagLayout: Layout {

private func width(
for indices: [Int],
subviews: Subviews
subviews: Subviews,
maxWidth: CGFloat
) -> CGFloat {
guard !indices.isEmpty else { return 0 }
let widths = indices.reduce(CGFloat.zero) { partialResult, index in
partialResult + subviews[index].sizeThatFits(.unspecified).width
partialResult + fittedSize(of: subviews[index], maxWidth: maxWidth).width
}
return widths + CGFloat(max(0, indices.count - 1)) * horizontalSpacing
}

private func fittedSize(
of subview: LayoutSubview,
maxWidth: CGFloat
) -> CGSize {
subview.sizeThatFits(
ProposedViewSize(width: maxWidth.isFinite ? maxWidth : nil, height: nil)
)
}

private var usesOverflowIndicator: Bool {
showsOverflowIndicator && (lineLimit ?? 0) > 0
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -181,77 +181,131 @@ private struct InfoSheetView: View {
private let calendar = Calendar.current

var body: some View {
NavigationStack {
List {
Section(String(localized: "todo_options_section", bundle: PresentationResources.bundle)) {
HStack {
Text(String(localized: "todo_category", bundle: PresentationResources.bundle))
Spacer()
ScrollView {
LazyVStack(alignment: .leading, spacing: 24) {
VStack(alignment: .leading, spacing: 0) {
optionRow(
systemImage: "tag.fill",
color: Color.accent,
title: String(localized: "todo_category", bundle: PresentationResources.bundle)
) {
Text(TodoCategoryItem(from: todo.category).localizedName)
.foregroundStyle(.secondary)
.foregroundStyle(Color.accent)
.lineLimit(1)
}

statusRow(
title: String(localized: "todo_completed", bundle: PresentationResources.bundle),
systemImage: todo.isCompleted ? "checkmark.circle.fill" : "circle",
color: todo.isCompleted ? .green : .secondary
)
Divider()

statusRow(
title: String(localized: "todo_pinned", bundle: PresentationResources.bundle),
systemImage: todo.isPinned ? "star.fill" : "star",
color: todo.isPinned ? .orange : .secondary
)
optionRow(
systemImage: "circle",
color: Color.textSecondary,
title: String(localized: "todo_completed", bundle: PresentationResources.bundle)
) {
Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundStyle(todo.isCompleted ? Color.accent : Color.textTertiary)
}

Divider()

HStack {
Text(String(localized: "todo_due_date", bundle: PresentationResources.bundle))
optionRow(
systemImage: "star.fill",
color: Color.orange,
title: String(localized: "todo_pinned", bundle: PresentationResources.bundle)
) {
Image(systemName: todo.isPinned ? "star.fill" : "star")
.foregroundStyle(todo.isPinned ? Color.orange : Color.textTertiary)
}

Spacer()
Divider()

optionRow(
systemImage: "calendar",
color: Color.textSecondary,
title: String(localized: "todo_due_date", bundle: PresentationResources.bundle)
) {
if let dueDate = todo.dueDate {
Tag(dueDateText(for: dueDate), isEditing: false)
.padding(.vertical, -4)
} else {
Text(String(localized: "todo_none", bundle: PresentationResources.bundle))
.foregroundStyle(.secondary)
.foregroundStyle(Color.textSecondary)
}
}
}
.background(Color.surface)
.clipShape(.rect(cornerRadius: 16))

Section(String(localized: "todo_tags", bundle: PresentationResources.bundle)) {
if todo.tags.isEmpty {
Text(String(localized: "todo_no_tags", bundle: PresentationResources.bundle))
.foregroundStyle(.secondary)
.padding(.vertical, 4)
} else {
TagList(todo.tags)
VStack(alignment: .leading, spacing: 12) {
Text(String(localized: "todo_tags", bundle: PresentationResources.bundle))
.font(.headline)
.padding(.horizontal, 16)

Group {
if todo.tags.isEmpty {
Text(String(localized: "todo_no_tags", bundle: PresentationResources.bundle))
.foregroundStyle(Color.textSecondary)
} else {
TagList(todo.tags, verticalSpacing: 4, horizontalSpacing: 4)
}
}
.padding(16)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.surface)
.clipShape(.rect(cornerRadius: 16))
}
}
.navigationTitle(String(localized: "todo_details", bundle: PresentationResources.bundle))
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarLeadingButton {
.padding(.horizontal, 16)
.padding(.bottom, 16)
}
.safeAreaInset(edge: .top, spacing: 0) { topBar }
.background(Color.appBackground.ignoresSafeArea())
.presentationDragIndicator(.visible)
}

private var topBar: some View {
ZStack {
Text(String(localized: "todo_details", bundle: PresentationResources.bundle))
.font(.headline)
HStack {
Button {
onClose()
} label: {
if #available(iOS 26.0, *) {
Image(systemName: "xmark")
} else {
Text(String(localized: "common_close", bundle: PresentationResources.bundle))
}
}
.topBarButtonStyle()
Spacer()
}
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
.background(Color.appBackground, ignoresSafeAreaEdges: .top)
}

@ViewBuilder
private func statusRow(
title: String,
private func optionRow<Trailing: View>(
systemImage: String,
color: Color
color: Color,
title: String,
@ViewBuilder trailing: () -> Trailing
) -> some View {
HStack {
HStack(spacing: 12) {
Image(systemName: systemImage)
.font(.title3)
.foregroundStyle(color)
.frame(width: 28, height: 28)

Text(title)
.font(.headline)
.layoutPriority(1)

Spacer()
Spacer(minLength: 8)

Image(systemName: systemImage)
.foregroundStyle(color)
trailing()
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
}

private func dueDateText(for dueDate: Date) -> String {
Expand Down
Loading