redesign: pinned document cards on Home

PinnedDocumentCard was a squat single-line HStack chip (12/9 padding,
cornerRadius 8, flat tertiary fill) - in a grid of several it read as a
row of tab/segmented-control buttons rather than distinct documents.
Restyled to the same tall VStack card shape as DocumentCardView (the
tab grids below it): icon row, 2-line headline title, relative-date
caption, subtle border. The one thing that still marks these as
pinned vs. the grids below is an accent-filled circular pin badge in
the corner, replacing the old plain gray pin glyph.

Also wired the new pointerCursorOnHover() modifier onto the pinned
card buttons, same as every other clickable row in the sidebar/lists.
This commit is contained in:
2026-08-20 22:26:21 +01:00
parent f01c2b9467
commit c36c70382b
2 changed files with 41 additions and 28 deletions
+1
View File
@@ -101,6 +101,7 @@ struct HomeView: View {
PinnedDocumentCard(document: document) PinnedDocumentCard(document: document)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.pointerCursorOnHover()
} }
} }
.padding(.horizontal, 24) .padding(.horizontal, 24)
+29 -17
View File
@@ -2,45 +2,57 @@
import SwiftUI import SwiftUI
import OutlineKit import OutlineKit
/// Deliberately distinct from `DocumentCardView` the pinned section is for /// Same tall-card shape as `DocumentCardView` (the tab grids below it) so the
/// a quick scan of a small curated set, not browsing, so this is a dense /// pinned section reads as a set of cards, not a row of tab-like chips the
/// single-line row rather than a tall card, with an explicit pin glyph so /// accent-filled pin badge is the one thing that marks these as pinned.
/// it doesn't read the same as the tab grids below it.
struct PinnedDocumentCard: View { struct PinnedDocumentCard: View {
@Environment(StarStore.self) private var starStore @Environment(StarStore.self) private var starStore
let document: OutlineDocument let document: OutlineDocument
var body: some View { var body: some View {
HStack(spacing: 10) { VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
if let emoji = document.emoji { if let emoji = document.emoji {
Text(emoji) Text(emoji)
.font(.title3) .font(.title2)
} else { } else {
Image(systemName: "doc.text") Image(systemName: "doc.text")
.font(.body) .font(.title3)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
Text(document.title.isEmpty ? "Untitled" : document.title) Spacer()
.font(.callout.weight(.medium))
.lineLimit(1)
Spacer(minLength: 0)
if starStore.isStarred(documentId: document.id) { if starStore.isStarred(documentId: document.id) {
Image(systemName: "star.fill") Image(systemName: "star.fill")
.font(.caption2) .font(.caption)
.foregroundStyle(.yellow) .foregroundStyle(.yellow)
} }
Image(systemName: "pin.fill") Image(systemName: "pin.fill")
.font(.caption2) .font(.caption2)
.foregroundStyle(.white)
.padding(5)
.background(Color.accentColor, in: Circle())
}
Text(document.title.isEmpty ? "Untitled" : document.title)
.font(.headline)
.lineLimit(2)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
Text(document.updatedAt, format: .relative(presentation: .named))
.font(.caption)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
.padding(.horizontal, 12) .padding(14)
.padding(.vertical, 9) .frame(maxWidth: .infinity, minHeight: 96, alignment: .topLeading)
.frame(maxWidth: .infinity, alignment: .leading) .background(.fill.tertiary, in: RoundedRectangle(cornerRadius: 12))
.background(.fill.tertiary, in: RoundedRectangle(cornerRadius: 8)) .overlay(
RoundedRectangle(cornerRadius: 12)
.strokeBorder(Color.primary.opacity(0.08), lineWidth: 1)
)
} }
} }
#endif #endif