wordeck-native/Sources/Core/Theme/CardSurface.swift
Till JS 542082772a refactor(big-bang): cards-native → wordeck-native
Code + Identity-Rename zur Vorbereitung auf Apple-Dev-Portal-Aktion
(Bundle ev.mana.wordeck, App-Group group.ev.mana.wordeck, AASA
applinks:wordeck.com). Build bleibt funktional, aber gegen die
neue text-only-API können image-occlusion-Creates 422 zurückgeben —
das wird mit der Wordeck-Native v1.0-Welle (parallele Apple-Aktion)
sauber gemacht.

Umbenennung:
- 41 Files: cardecky/Cardecky → wordeck/Wordeck (Display, Strings,
  Kommentare)
- 57 Files: CardsNative → WordeckNative, CardsAPI → WordeckAPI,
  CardsTheme → WordeckTheme, CardsBrand → WordeckBrand, CardsWidget →
  WordeckWidget, CardsDueWidget → WordeckDueWidget
- Bundle-ID ev.mana.cardecky → ev.mana.wordeck (project.yml,
  Info.plist, entitlements, Keychain-Service, App-Group)
- AASA applinks:cardecky.mana.how → applinks:wordeck.com
- API-Base cardecky-api.mana.how → api.wordeck.com
- 10 Files renamed (App-Entry, API-Extensions, Theme, Widget,
  Entitlements, Tests)
- xcodeproj regenerated via xcodegen → WordeckNative.xcodeproj
- MaskRegionsTests.swift gelöscht (image-occlusion entfällt mit
  Wordeck text-only)

Forgejo-Repo git.mana.how/till/cards-native → wordeck-native umbenannt
(Auto-Redirect aktiv). Lokales Verzeichnis Code/cards-native/ bleibt
vorerst — wird beim nächsten Apple-Setup mit Bundle-Test umbenannt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 23:10:42 +02:00

108 lines
3.2 KiB
Swift

import SwiftUI
/// Wiederverwendbare Karten-Hülle in drei Größen entspricht den
/// Web-`CardSurface.svelte`-Varianten. Konsistenter Border-Radius (14pt),
/// gleicher Border-Stil, gleiche Shadow-Behandlung über alle Größen,
/// optional ein linker Color-Accent-Streifen.
///
/// Spec aus `cards/apps/web/src/lib/components/CardSurface.svelte`:
/// - Alle Größen Border-Radius 0.875rem (14pt)
/// - Border 1px hsl(--color-border)
/// - Background hsl(--color-surface)
/// - Aspect-Ratio 5/7 für `.md` und `.hero`, fix für `.lg`
struct CardSurface<Content: View>: View {
enum Size {
case md // Deck-Tile in der Liste (max-width 18rem)
case lg // Fan-Detail (12rem x 16.8rem)
case hero // Study-Lernkarte (max-width 24rem)
}
enum Elevation {
case flat // Subtle shadow
case standard // Default Karten-Shadow
case raised // Study-Hero
}
let size: Size
let elevation: Elevation
let colorAccentHex: String?
let content: () -> Content
init(
size: Size = .md,
elevation: Elevation = .standard,
colorAccentHex: String? = nil,
@ViewBuilder content: @escaping () -> Content
) {
self.size = size
self.elevation = elevation
self.colorAccentHex = colorAccentHex
self.content = content
}
var body: some View {
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: 14, style: .continuous)
.fill(WordeckTheme.surface)
.overlay(
RoundedRectangle(cornerRadius: 14, style: .continuous)
.stroke(WordeckTheme.border, lineWidth: 1)
)
if let colorAccentHex {
Color.swatchFromHex(colorAccentHex)
.frame(width: 6)
.clipShape(
UnevenRoundedRectangle(
topLeadingRadius: 14,
bottomLeadingRadius: 14,
bottomTrailingRadius: 0,
topTrailingRadius: 0,
style: .continuous
)
)
}
content()
.padding(EdgeInsets(top: 16, leading: 22, bottom: 18, trailing: 16))
}
.frame(maxWidth: maxWidth)
.aspectRatio(aspectRatio, contentMode: .fit)
.shadow(color: shadowColor, radius: shadowRadius, x: 0, y: shadowY)
}
private var maxWidth: CGFloat? {
switch size {
case .md: 288 // 18rem
case .lg: 192 // 12rem
case .hero: 384 // 24rem
}
}
private var aspectRatio: CGFloat? {
switch size {
case .md, .hero: 5.0 / 7.0
case .lg: 12.0 / 16.8
}
}
private var shadowColor: Color {
WordeckTheme.foreground.opacity(elevation == .raised ? 0.18 : 0.08)
}
private var shadowRadius: CGFloat {
switch elevation {
case .flat: 3
case .standard: 8
case .raised: 18
}
}
private var shadowY: CGFloat {
switch elevation {
case .flat: 1
case .standard: 4
case .raised: 12
}
}
}