Bedrock/Sources/Bedrock/Views/Settings/SettingsToggle.swift
Matt Bruce 3e5e6b7447 feature updates
Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
2026-02-07 10:58:42 -06:00

146 lines
4.1 KiB
Swift

//
// SettingsToggle.swift
// Bedrock
//
// A toggle setting row with title and subtitle.
//
import SwiftUI
/// A toggle setting row with title and subtitle.
///
/// Use this for boolean settings that can be turned on or off.
///
/// ```swift
/// // Basic toggle (dark theme)
/// SettingsToggle(
/// title: "Dark Mode",
/// subtitle: "Use dark appearance",
/// isOn: $settings.darkMode
/// )
///
/// // Light theme toggle
/// SettingsToggle(
/// title: "Notifications",
/// subtitle: "Receive push notifications",
/// isOn: $settings.notifications,
/// textColor: AppText.primary,
/// secondaryTextColor: AppText.secondary
/// )
///
/// // Premium toggle with crown icon
/// SettingsToggle(
/// title: "Flash Sync",
/// subtitle: "Use ring light color for flash",
/// isOn: $settings.flashSync,
/// titleAccessory: Image(systemName: "crown.fill")
/// .foregroundStyle(.orange)
/// )
/// ```
public struct SettingsToggle<Accessory: View>: View {
/// The main title text.
public let title: String
/// The subtitle/description text.
public let subtitle: String
/// Binding to the toggle state.
@Binding public var isOn: Bool
/// The accent color for the toggle.
public let accentColor: Color
/// Optional accessory view shown after the title (e.g., crown icon for premium).
public let titleAccessory: Accessory?
/// Creates a settings toggle.
/// - Parameters:
/// - title: The main title.
/// - subtitle: The subtitle description.
/// - isOn: Binding to toggle state.
/// - accentColor: The accent color (default: primary accent).
/// - titleAccessory: Optional view after title (e.g., premium crown).
public init(
title: String,
subtitle: String,
isOn: Binding<Bool>,
accentColor: Color = .Accent.primary,
@ViewBuilder titleAccessory: () -> Accessory? = { nil as EmptyView? }
) {
self.title = title
self.subtitle = subtitle
self._isOn = isOn
self.accentColor = accentColor
self.titleAccessory = titleAccessory()
}
public var body: some View {
Toggle(isOn: $isOn) {
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
HStack(spacing: Design.Spacing.xSmall) {
Text(title).styled(.subheadingEmphasis)
if let titleAccessory {
titleAccessory
.font(Typography.caption2.font)
}
}
Text(subtitle).styled(.subheading, emphasis: .secondary)
}
}
.tint(accentColor)
.padding(.vertical, Design.Spacing.medium)
.padding(.horizontal, Design.Spacing.medium)
.sensoryFeedback(.impact(flexibility: .soft), trigger: isOn)
}
}
// MARK: - Convenience Initializer
extension SettingsToggle where Accessory == EmptyView {
/// Creates a settings toggle without a title accessory.
public init(
title: String,
subtitle: String,
isOn: Binding<Bool>,
accentColor: Color = .Accent.primary
) {
self.title = title
self.subtitle = subtitle
self._isOn = isOn
self.accentColor = accentColor
self.titleAccessory = nil
}
}
// MARK: - Preview
#Preview {
VStack(spacing: Design.Spacing.medium) {
SettingsToggle(
title: "Sound Effects",
subtitle: "Play sounds for events",
isOn: .constant(true)
)
SettingsToggle(
title: "Notifications",
subtitle: "Receive push notifications",
isOn: .constant(false)
)
SettingsToggle(
title: "Flash Sync",
subtitle: "Use ring light color for screen flash",
isOn: .constant(true),
titleAccessory: {
Image(systemName: "crown.fill")
.foregroundStyle(.orange)
}
)
}
.padding()
.background(Color.Surface.overlay)
}