mana-swift-ui/Sources/ManaOnboardingUI/ManaOnboardingPage.swift
Till JS fe92bffc59 ManaPill und Onboarding-Zeile: auch eigene Bilder statt nur SF-Symbole
ManaPill(_:image:variant:size:action:) und Row(katalogSymbol:) für Apps
mit eigenem Zeichensatz (seepuls). Rein additiv: bestehende Aufrufe mit
systemImage/symbol laufen unverändert, kein Init wird mehrdeutig.

Sitzung: 79964fc4
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0111BCGu1WXLkJzY5CGKXdC6
2026-09-14 19:47:02 +02:00

95 lines
3.6 KiB
Swift

import SwiftUI
/// Eine Seite im Einstiegs-Flow — im Stil von Apples „Neue Funktionen“-
/// Sheet: ein großer Titel, optional ein kurzer Vorspann, darunter eine
/// **vertikale Liste von Zeilen** (Icon links, Titel + Text rechts), und
/// pro Seite genau **ein** Weiter-Button (der Container rendert ihn).
///
/// Inhalt ist **app-geliefert** — das Paket weiß nichts über Vorsätze,
/// Decks oder Memos. Eine App baut `[ManaOnboardingPage]` und übergibt
/// sie an ``SwiftUICore/View/manaOnboarding(storageKey:pages:onFinish:)``.
/// Die Optik (Farben) kommt aus `@Environment(\.manaTheme)`, das die App
/// sowieso setzt — dadurch sieht der Flow flottenweit einheitlich aus,
/// aber im Signatur-Theme der jeweiligen App.
///
/// **Bewusst ohne Berechtigungs-Schritte.** `ManaOnboardingUI` fragt
/// **nie** System-Rechte ab — Berechtigungen hängen an konkreten Features
/// und werden *just-in-time* erfragt, wenn das Feature zum ersten Mal
/// genutzt wird. Das Onboarding **informiert** nur. (Werte-Entscheidung
/// 2026-06-05: kein Extrahieren auf Vorrat.)
public struct ManaOnboardingPage: Identifiable, Sendable {
public let id: String
/// Große Überschrift oben auf der Seite.
public let title: String
/// Früherer Vorspann unter dem Titel. **Wird nicht mehr angezeigt**
/// (Design-Entscheidung 2026-06-09: Onboarding-Seiten tragen nur
/// Überschrift + Karten, keinen freien Text). Param bleibt aus
/// Quellkompatibilität bestehen, wird aber ignoriert.
public let intro: String?
/// Die Inhalts-Zeilen (Apple-„Neue Funktionen“-Liste).
public let rows: [Row]
/// Frühere Fußnote über dem Button. **Wird nicht mehr angezeigt**
/// (siehe `intro`). Param bleibt aus Quellkompatibilität, ignoriert.
public let footnote: String?
public init(
id: String? = nil,
title: String,
intro: String? = nil,
rows: [Row],
footnote: String? = nil
) {
self.id = id ?? title
self.title = title
self.intro = intro
self.rows = rows
self.footnote = footnote
}
/// Eine Zeile: Symbol (links, brand-getönt) + Titel + Beschreibung.
public struct Row: Identifiable, Sendable {
/// Woher das Symbol kommt.
public enum Quelle: Sendable, Hashable {
/// SF-Symbol.
case system
/// Symbol oder Bild aus dem Asset-Katalog der App (eigener Zeichensatz).
case katalog
}
public let id: String
/// SF-Symbol-Name oder, bei ``Quelle/katalog``, Asset-Name.
public let symbol: String
public let quelle: Quelle
public let title: String
public let text: String
public init(id: String? = nil, symbol: String, title: String, text: String) {
self.init(id: id, symbol: symbol, quelle: .system, title: title, text: text)
}
/// Zeile mit einem Symbol aus dem Asset-Katalog der App.
public init(id: String? = nil, katalogSymbol: String, title: String, text: String) {
self.init(id: id, symbol: katalogSymbol, quelle: .katalog, title: title, text: text)
}
private init(id: String?, symbol: String, quelle: Quelle, title: String, text: String) {
self.id = id ?? title
self.symbol = symbol
self.quelle = quelle
self.title = title
self.text = text
}
/// Das Bild der Zeile.
public var bild: Image {
switch quelle {
case .system: Image(systemName: symbol)
case .katalog: Image(symbol)
}
}
}
}