Geteilte Listen-Bausteine, drift-sicher als Pendant zur Compose-Lib: - ManaSectionHeader: großer (.title2/SemiBold) Abschnitts-Header für scrollende Listen, optional einklappbar (Chevron). - ManaPinnedGlyph: gezeichneter Fünfzack-Stern (geteilte Geometrie via ManaStarShape), kein SF-Symbol → pixelgleich zum Android-Pendant. - ManaScreenHeader: Titel jetzt fett (.title.bold). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
99 lines
3.7 KiB
Swift
99 lines
3.7 KiB
Swift
import ManaTokens
|
|
import SwiftUI
|
|
|
|
/// Flotten-einheitlicher Seiten-Header — natives Pendant zur Android-
|
|
/// `ManaScreenHeader` (mana-kotlin-ui). **Der Stil ist hier zentral
|
|
/// festgelegt**, damit der Titel über alle nativen mana-Apps gleich aussieht:
|
|
///
|
|
/// - Titel: `.title`, Akzentfarbe (`primary`), **unterstrichen** — die
|
|
/// Marken-Signatur (Referenz: memoro „Deine Memos", pageta „Meine Artikel").
|
|
/// - Untertitel (optional): `.subheadline`, gedämpfte Vordergrundfarbe.
|
|
/// - **Links-bündig**; Titel-Spalte links, optionaler `trailing`-Slot rechts
|
|
/// oben (typisch der Konto-Avatar) — beide auf gleicher Höhe.
|
|
/// - Padding zentral: 20 horizontal, 12 vertikal.
|
|
///
|
|
/// Vor dieser Komponente malte jede App ihren Titel inline mit eigener Schrift,
|
|
/// Ausrichtung und Padding. Apps liefern jetzt nur noch Titel/Untertitel/Avatar.
|
|
///
|
|
/// Die Farben kommen aus der aktiven Theme-Variant über
|
|
/// `@Environment(\.manaTheme)`; die App injiziert sie an der Root via
|
|
/// `.manaTheme(_:)`.
|
|
///
|
|
/// ```swift
|
|
/// ManaScreenHeader("Deine Memos", subtitle: "local-first") {
|
|
/// ManaAccountAvatarButton(auth: auth) { showAccount = true }
|
|
/// }
|
|
/// ```
|
|
public struct ManaScreenHeader<Trailing: View>: View {
|
|
@Environment(\.manaTheme) private var theme
|
|
|
|
private let title: String
|
|
private let subtitle: String?
|
|
private let titleColor: Color?
|
|
private let horizontalPadding: CGFloat
|
|
private let verticalPadding: CGFloat
|
|
private let trailing: Trailing
|
|
|
|
/// - Parameters:
|
|
/// - title: Seitentitel (unterstrichen, Akzentfarbe).
|
|
/// - subtitle: optionaler erklärender Untertitel.
|
|
/// - titleColor: Titelfarbe-Override. `nil` → Theme-`primary`. Für Apps
|
|
/// mit eigenem Header-Hintergrund (z. B. weißer Titel auf dunkler Fläche).
|
|
/// - horizontalPadding: seitliches Padding (Default 20).
|
|
/// - verticalPadding: vertikales Padding (Default 12).
|
|
/// - trailing: rechts-oben angesetzter Slot, typisch der Konto-Avatar.
|
|
public init(
|
|
_ title: String,
|
|
subtitle: String? = nil,
|
|
titleColor: Color? = nil,
|
|
horizontalPadding: CGFloat = 20,
|
|
verticalPadding: CGFloat = 12,
|
|
@ViewBuilder trailing: () -> Trailing
|
|
) {
|
|
self.title = title
|
|
self.subtitle = subtitle
|
|
self.titleColor = titleColor
|
|
self.horizontalPadding = horizontalPadding
|
|
self.verticalPadding = verticalPadding
|
|
self.trailing = trailing()
|
|
}
|
|
|
|
public var body: some View {
|
|
HStack(alignment: .top, spacing: 12) {
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(title)
|
|
.font(.title.weight(.bold))
|
|
.underline()
|
|
.foregroundStyle(titleColor ?? theme.colors.primary)
|
|
if let subtitle {
|
|
Text(subtitle)
|
|
.font(.subheadline)
|
|
.foregroundStyle(theme.colors.mutedForeground)
|
|
}
|
|
}
|
|
Spacer(minLength: 0)
|
|
trailing
|
|
}
|
|
.padding(.horizontal, horizontalPadding)
|
|
.padding(.vertical, verticalPadding)
|
|
}
|
|
}
|
|
|
|
public extension ManaScreenHeader where Trailing == EmptyView {
|
|
/// Header ohne `trailing`-Slot (kein Avatar/Aktion rechts).
|
|
init(
|
|
_ title: String,
|
|
subtitle: String? = nil,
|
|
titleColor: Color? = nil,
|
|
horizontalPadding: CGFloat = 20,
|
|
verticalPadding: CGFloat = 12
|
|
) {
|
|
self.init(
|
|
title,
|
|
subtitle: subtitle,
|
|
titleColor: titleColor,
|
|
horizontalPadding: horizontalPadding,
|
|
verticalPadding: verticalPadding
|
|
) { EmptyView() }
|
|
}
|
|
}
|