swift-layout/Sources/LilithLayout/Divider.swift

367 lines
12 KiB
Swift
Executable file

// Divider.swift
// iOS UI Components - Layout Components
//
// Visual separator (horizontal and vertical)
import SwiftUI
import LilithDesignTokens
/// Divider component
///
/// Visual separator with customizable thickness and color.
///
/// Example:
/// ```swift
/// Divider()
/// Divider(thickness: 2, color: AppColors.primary)
/// Divider(orientation: .vertical)
/// ```
public struct Divider: View {
// MARK: - Orientation
public enum Orientation {
case horizontal
case vertical
}
// MARK: - Properties
private let orientation: Orientation
private let thickness: CGFloat
private let color: Color
private let spacing: CGFloat
// MARK: - Initialization
/// Create a divider
/// - Parameters:
/// - orientation: Divider orientation (default: horizontal)
/// - thickness: Line thickness (default: 1)
/// - color: Line color (default: border)
/// - spacing: Spacing around divider (default: 0)
public init(
orientation: Orientation = .horizontal,
thickness: CGFloat = 1,
color: Color = AppColors.border,
spacing: CGFloat = 0
) {
self.orientation = orientation
self.thickness = thickness
self.color = color
self.spacing = spacing
}
// MARK: - Body
public var body: some View {
switch orientation {
case .horizontal:
Rectangle()
.fill(color)
.frame(height: thickness)
.padding(.vertical, spacing)
.frame(maxWidth: .infinity)
case .vertical:
Rectangle()
.fill(color)
.frame(width: thickness)
.padding(.horizontal, spacing)
.frame(maxHeight: .infinity)
}
}
}
// MARK: - Divider Variants
extension Divider {
/// Thick divider (2pt)
public static func thick(
orientation: Orientation = .horizontal,
color: Color = AppColors.border
) -> Divider {
Divider(orientation: orientation, thickness: 2, color: color)
}
/// Thin divider (0.5pt)
public static func thin(
orientation: Orientation = .horizontal,
color: Color = AppColors.border
) -> Divider {
Divider(orientation: orientation, thickness: 0.5, color: color)
}
/// Primary colored divider
public static func primary(
orientation: Orientation = .horizontal,
thickness: CGFloat = 1
) -> Divider {
Divider(orientation: orientation, thickness: thickness, color: AppColors.primary)
}
/// Divider with spacing
public static func spaced(
orientation: Orientation = .horizontal,
spacing: CGFloat = AppSpacing.md
) -> Divider {
Divider(orientation: orientation, spacing: spacing)
}
}
// MARK: - Preview Provider
#if DEBUG
struct Divider_Previews: PreviewProvider {
static var previews: some View {
ScrollView {
VStack(spacing: AppSpacing.xl) {
// Basic horizontal dividers
VStack(alignment: .leading, spacing: AppSpacing.md) {
Text("Horizontal Dividers")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
Text("Standard (1pt)")
Divider()
Text("Thin (0.5pt)")
Divider.thin()
Text("Thick (2pt)")
Divider.thick()
Text("Primary Color")
Divider.primary()
Text("With Spacing")
Divider.spaced()
}
.padding()
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.card))
// Vertical dividers
VStack(alignment: .leading, spacing: AppSpacing.md) {
Text("Vertical Dividers")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
HStack(spacing: AppSpacing.md) {
Text("Left")
Divider(orientation: .vertical)
.frame(height: 40)
Text("Middle")
Divider.thick(orientation: .vertical)
.frame(height: 40)
Text("Right")
}
}
.padding()
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.card))
// In context - List items
VStack(alignment: .leading, spacing: 0) {
Text("List with Dividers")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
.padding()
ForEach(0..<4) { index in
VStack(spacing: 0) {
HStack {
Text("Item \(index + 1)")
.font(AppTypography.body())
.foregroundColor(AppColors.textPrimary)
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(AppColors.textTertiary)
}
.padding()
if index < 3 {
Divider()
.padding(.leading)
}
}
}
}
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.card))
// In context - Card sections
VStack(alignment: .leading, spacing: 0) {
Text("Card Sections")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
.padding()
VStack(alignment: .leading, spacing: AppSpacing.sm) {
Text("Account Information")
.font(AppTypography.body(weight: .semibold))
.foregroundColor(AppColors.textPrimary)
Text("Manage your account details")
.font(AppTypography.caption())
.foregroundColor(AppColors.textSecondary)
}
.padding()
Divider()
VStack(alignment: .leading, spacing: AppSpacing.sm) {
Text("Privacy Settings")
.font(AppTypography.body(weight: .semibold))
.foregroundColor(AppColors.textPrimary)
Text("Control your privacy preferences")
.font(AppTypography.caption())
.foregroundColor(AppColors.textSecondary)
}
.padding()
Divider()
VStack(alignment: .leading, spacing: AppSpacing.sm) {
Text("Security")
.font(AppTypography.body(weight: .semibold))
.foregroundColor(AppColors.textPrimary)
Text("Protect your account")
.font(AppTypography.caption())
.foregroundColor(AppColors.textSecondary)
}
.padding()
}
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.card))
// In context - Stats grid
VStack(alignment: .leading, spacing: AppSpacing.md) {
Text("Stats with Dividers")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
HStack(spacing: 0) {
VStack(spacing: AppSpacing.xs) {
Text("1,234")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
Text("Followers")
.font(AppTypography.caption())
.foregroundColor(AppColors.textSecondary)
}
.frame(maxWidth: .infinity)
Divider(orientation: .vertical)
.frame(height: 60)
VStack(spacing: AppSpacing.xs) {
Text("567")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
Text("Following")
.font(AppTypography.caption())
.foregroundColor(AppColors.textSecondary)
}
.frame(maxWidth: .infinity)
Divider(orientation: .vertical)
.frame(height: 60)
VStack(spacing: AppSpacing.xs) {
Text("89")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
Text("Posts")
.font(AppTypography.caption())
.foregroundColor(AppColors.textSecondary)
}
.frame(maxWidth: .infinity)
}
.padding()
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.card))
}
// In context - Toolbar sections
HStack(spacing: 0) {
Button {
print("Action 1")
} label: {
Image(systemName: "square.and.arrow.up")
.frame(maxWidth: .infinity)
.padding()
}
Divider.thin(orientation: .vertical)
Button {
print("Action 2")
} label: {
Image(systemName: "heart")
.frame(maxWidth: .infinity)
.padding()
}
Divider.thin(orientation: .vertical)
Button {
print("Action 3")
} label: {
Image(systemName: "bookmark")
.frame(maxWidth: .infinity)
.padding()
}
Divider.thin(orientation: .vertical)
Button {
print("Action 4")
} label: {
Image(systemName: "ellipsis")
.frame(maxWidth: .infinity)
.padding()
}
}
.foregroundColor(AppColors.textPrimary)
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.card))
// Color variations
VStack(alignment: .leading, spacing: AppSpacing.md) {
Text("Color Variations")
.font(AppTypography.h3())
.foregroundColor(AppColors.textPrimary)
Text("Default")
Divider()
Text("Primary")
Divider(color: AppColors.primary)
Text("Success")
Divider(color: AppColors.Semantic.success)
Text("Warning")
Divider(color: AppColors.Semantic.warning)
Text("Error")
Divider(color: AppColors.Semantic.error)
}
.padding()
.background(AppColors.surface)
.clipShape(RoundedRectangle(cornerRadius: AppRadius.card))
}
.padding()
}
.background(AppColors.background)
.previewDisplayName("Divider States")
}
}
#endif