Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
c9b1b3cf62
commit
3307ba5433
8
PRD.md
8
PRD.md
@ -376,17 +376,13 @@ TheNoiseClock/
|
|||||||
│ │ ├── Design/
|
│ │ ├── Design/
|
||||||
│ │ │ ├── BrandingConfig.swift # Bedrock branding + launch config
|
│ │ │ ├── BrandingConfig.swift # Bedrock branding + launch config
|
||||||
│ │ │ ├── AppConstants.swift # App-wide constants and configuration
|
│ │ │ ├── AppConstants.swift # App-wide constants and configuration
|
||||||
│ │ │ ├── UIConstants.swift # UI constants namespace
|
│ │ │ ├── NoiseClockTheme.swift # Bedrock app color theme
|
||||||
│ │ │ ├── UIConstants+Colors.swift # Theme-backed colors
|
│ │ │ ├── Design+NoiseClock.swift # App-specific Design extensions
|
||||||
│ │ │ ├── UIConstants+Layout.swift # Spacing/radius/opacity
|
|
||||||
│ │ │ ├── UIConstants+Animation.swift # Animation curves
|
|
||||||
│ │ │ └── Fonts/
|
│ │ │ └── Fonts/
|
||||||
│ │ │ ├── Font.Design.swift
|
│ │ │ ├── Font.Design.swift
|
||||||
│ │ │ ├── Font.Weight.swift
|
│ │ │ ├── Font.Weight.swift
|
||||||
│ │ │ ├── FontFamily.swift
|
│ │ │ ├── FontFamily.swift
|
||||||
│ │ │ └── FontUtils.swift
|
│ │ │ └── FontUtils.swift
|
||||||
│ │ ├── Theme/
|
|
||||||
│ │ │ └── NoiseClockTheme.swift # Bedrock app color theme
|
|
||||||
│ │ ├── Extensions/
|
│ │ ├── Extensions/
|
||||||
│ │ │ ├── Color+Extensions.swift # Color utilities and extensions
|
│ │ │ ├── Color+Extensions.swift # Color utilities and extensions
|
||||||
│ │ │ ├── Date+Extensions.swift # Date formatting and utilities
|
│ │ │ ├── Date+Extensions.swift # Date formatting and utilities
|
||||||
|
|||||||
@ -86,7 +86,7 @@ xcodebuild -project TheNoiseClock/TheNoiseClock.xcodeproj -scheme TheNoiseClock
|
|||||||
|
|
||||||
The app uses Bedrock for theme tokens, settings UI, and the launch experience.
|
The app uses Bedrock for theme tokens, settings UI, and the launch experience.
|
||||||
|
|
||||||
- Theme: `TheNoiseClock/Shared/Theme/NoiseClockTheme.swift`
|
- Theme: `TheNoiseClock/Shared/Design/NoiseClockTheme.swift`
|
||||||
- Branding config: `TheNoiseClock/Shared/Design/BrandingConfig.swift`
|
- Branding config: `TheNoiseClock/Shared/Design/BrandingConfig.swift`
|
||||||
- Launch screen: `TheNoiseClock/Resources/LaunchScreen.storyboard`
|
- Launch screen: `TheNoiseClock/Resources/LaunchScreen.storyboard`
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ struct AddAlarmView: View {
|
|||||||
NavigationLink(destination: LabelEditView(label: $alarmLabel)) {
|
NavigationLink(destination: LabelEditView(label: $alarmLabel)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "textformat")
|
Image(systemName: "textformat")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Label")
|
Text("Label")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -50,7 +50,7 @@ struct AddAlarmView: View {
|
|||||||
NavigationLink(destination: NotificationMessageEditView(message: $notificationMessage)) {
|
NavigationLink(destination: NotificationMessageEditView(message: $notificationMessage)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "message")
|
Image(systemName: "message")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Message")
|
Text("Message")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -64,7 +64,7 @@ struct AddAlarmView: View {
|
|||||||
NavigationLink(destination: SoundSelectionView(selectedSound: $selectedSoundName)) {
|
NavigationLink(destination: SoundSelectionView(selectedSound: $selectedSoundName)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "music.note")
|
Image(systemName: "music.note")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Sound")
|
Text("Sound")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -77,7 +77,7 @@ struct AddAlarmView: View {
|
|||||||
NavigationLink(destination: SnoozeSelectionView(snoozeDuration: $snoozeDuration)) {
|
NavigationLink(destination: SnoozeSelectionView(snoozeDuration: $snoozeDuration)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "clock.arrow.circlepath")
|
Image(systemName: "clock.arrow.circlepath")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Snooze")
|
Text("Snooze")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -96,7 +96,7 @@ struct AddAlarmView: View {
|
|||||||
Button("Cancel") {
|
Button("Cancel") {
|
||||||
isPresented = false
|
isPresented = false
|
||||||
}
|
}
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
@ -116,7 +116,7 @@ struct AddAlarmView: View {
|
|||||||
isPresented = false
|
isPresented = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// Component for displaying individual alarm row
|
/// Component for displaying individual alarm row
|
||||||
struct AlarmRowView: View {
|
struct AlarmRowView: View {
|
||||||
@ -18,18 +19,18 @@ struct AlarmRowView: View {
|
|||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: UIConstants.Spacing.extraSmall) {
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
||||||
Text(alarm.formattedTime())
|
Text(alarm.formattedTime())
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundColor(UIConstants.Colors.primaryText)
|
.foregroundColor(AppTextColors.primary)
|
||||||
|
|
||||||
Text(alarm.label)
|
Text(alarm.label)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(UIConstants.Colors.secondaryText)
|
.foregroundColor(AppTextColors.secondary)
|
||||||
|
|
||||||
Text("• \(AlarmSoundService.shared.getSoundDisplayName(alarm.soundName))")
|
Text("• \(AlarmSoundService.shared.getSoundDisplayName(alarm.soundName))")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(UIConstants.Colors.secondaryText)
|
.foregroundColor(AppTextColors.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// Empty state view for when no alarms are configured
|
/// Empty state view for when no alarms are configured
|
||||||
struct EmptyAlarmsView: View {
|
struct EmptyAlarmsView: View {
|
||||||
@ -15,7 +16,7 @@ struct EmptyAlarmsView: View {
|
|||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: UIConstants.Spacing.medium) {
|
VStack(spacing: Design.Spacing.medium) {
|
||||||
// Icon
|
// Icon
|
||||||
Image(systemName: "alarm")
|
Image(systemName: "alarm")
|
||||||
.font(.largeTitle)
|
.font(.largeTitle)
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// View for editing alarm label
|
/// View for editing alarm label
|
||||||
struct LabelEditView: View {
|
struct LabelEditView: View {
|
||||||
@ -13,15 +14,15 @@ struct LabelEditView: View {
|
|||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: UIConstants.Spacing.large) {
|
VStack(spacing: Design.Spacing.large) {
|
||||||
TextField("Alarm Label", text: $label)
|
TextField("Alarm Label", text: $label)
|
||||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large)
|
.contentPadding(horizontal: Design.Spacing.large)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.navigationTitle("Label")
|
.navigationTitle("Label")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.contentPadding(vertical: UIConstants.Spacing.large)
|
.contentPadding(vertical: Design.Spacing.large)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// View for editing alarm notification message
|
/// View for editing alarm notification message
|
||||||
struct NotificationMessageEditView: View {
|
struct NotificationMessageEditView: View {
|
||||||
@ -13,13 +14,13 @@ struct NotificationMessageEditView: View {
|
|||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: UIConstants.Spacing.large) {
|
VStack(spacing: Design.Spacing.large) {
|
||||||
TextField("Notification message", text: $message)
|
TextField("Notification message", text: $message)
|
||||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large)
|
.contentPadding(horizontal: Design.Spacing.large)
|
||||||
|
|
||||||
// Preview section
|
// Preview section
|
||||||
VStack(alignment: .leading, spacing: UIConstants.Spacing.small) {
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
Text("Preview:")
|
Text("Preview:")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
@ -37,13 +38,13 @@ struct NotificationMessageEditView: View {
|
|||||||
.background(Color(.systemGray6))
|
.background(Color(.systemGray6))
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
}
|
}
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large)
|
.contentPadding(horizontal: Design.Spacing.large)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.navigationTitle("Message")
|
.navigationTitle("Message")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.contentPadding(vertical: UIConstants.Spacing.large)
|
.contentPadding(vertical: Design.Spacing.large)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ struct SnoozeSelectionView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
if snoozeDuration == duration {
|
if snoozeDuration == duration {
|
||||||
Image(systemName: "checkmark")
|
Image(systemName: "checkmark")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
|
|||||||
@ -30,7 +30,7 @@ struct SoundSelectionView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
if selectedSound == sound.fileName {
|
if selectedSound == sound.fileName {
|
||||||
Image(systemName: "checkmark")
|
Image(systemName: "checkmark")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
@ -56,7 +56,7 @@ struct SoundSelectionView: View {
|
|||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: isPlaying && currentlyPlayingSound == selectedSound ? "stop.fill" : "play.fill")
|
Image(systemName: isPlaying && currentlyPlayingSound == selectedSound ? "stop.fill" : "play.fill")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ struct TimeUntilAlarmSection: View {
|
|||||||
VStack(spacing: 4) {
|
VStack(spacing: 4) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "calendar")
|
Image(systemName: "calendar")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
Text(timeUntilAlarm)
|
Text(timeUntilAlarm)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|||||||
@ -55,7 +55,7 @@ struct EditAlarmView: View {
|
|||||||
NavigationLink(destination: LabelEditView(label: $alarmLabel)) {
|
NavigationLink(destination: LabelEditView(label: $alarmLabel)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "textformat")
|
Image(systemName: "textformat")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Label")
|
Text("Label")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -68,7 +68,7 @@ struct EditAlarmView: View {
|
|||||||
NavigationLink(destination: NotificationMessageEditView(message: $notificationMessage)) {
|
NavigationLink(destination: NotificationMessageEditView(message: $notificationMessage)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "message")
|
Image(systemName: "message")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Message")
|
Text("Message")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -82,7 +82,7 @@ struct EditAlarmView: View {
|
|||||||
NavigationLink(destination: SoundSelectionView(selectedSound: $selectedSoundName)) {
|
NavigationLink(destination: SoundSelectionView(selectedSound: $selectedSoundName)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "music.note")
|
Image(systemName: "music.note")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Sound")
|
Text("Sound")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -95,7 +95,7 @@ struct EditAlarmView: View {
|
|||||||
NavigationLink(destination: SnoozeSelectionView(snoozeDuration: $snoozeDuration)) {
|
NavigationLink(destination: SnoozeSelectionView(snoozeDuration: $snoozeDuration)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "clock.arrow.circlepath")
|
Image(systemName: "clock.arrow.circlepath")
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.frame(width: 24)
|
.frame(width: 24)
|
||||||
Text("Snooze")
|
Text("Snooze")
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -114,7 +114,7 @@ struct EditAlarmView: View {
|
|||||||
Button("Cancel") {
|
Button("Cancel") {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
@ -136,7 +136,7 @@ struct EditAlarmView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.foregroundColor(UIConstants.Colors.accentColor)
|
.foregroundColor(AppAccent.primary)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import Combine
|
|||||||
import Observation
|
import Observation
|
||||||
import AudioPlaybackKit
|
import AudioPlaybackKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// ViewModel for clock display and management
|
/// ViewModel for clock display and management
|
||||||
@Observable
|
@Observable
|
||||||
@ -60,7 +61,7 @@ class ClockViewModel {
|
|||||||
|
|
||||||
// MARK: - Public Interface
|
// MARK: - Public Interface
|
||||||
func toggleDisplayMode() {
|
func toggleDisplayMode() {
|
||||||
withAnimation(UIConstants.AnimationCurves.bouncy) {
|
withAnimation(Design.Animation.spring(bounce: Design.Animation.springBounce)) {
|
||||||
isDisplayMode.toggle()
|
isDisplayMode.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// Container component that handles the main clock display layout and scaling
|
/// Container component that handles the main clock display layout and scaling
|
||||||
struct ClockDisplayContainer: View {
|
struct ClockDisplayContainer: View {
|
||||||
@ -20,7 +21,7 @@ struct ClockDisplayContainer: View {
|
|||||||
return GeometryReader { geometry in
|
return GeometryReader { geometry in
|
||||||
let isPortrait = geometry.size.height >= geometry.size.width
|
let isPortrait = geometry.size.height >= geometry.size.width
|
||||||
let hasOverlay = style.showBattery || style.showDate
|
let hasOverlay = style.showBattery || style.showDate
|
||||||
let topSpacing = hasOverlay ? (isPortrait ? UIConstants.Spacing.huge : UIConstants.Spacing.large) : 0
|
let topSpacing = hasOverlay ? (isPortrait ? Design.Spacing.xxLarge : Design.Spacing.large) : 0
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
// Top spacing to account for overlay
|
// Top spacing to account for overlay
|
||||||
@ -50,7 +51,7 @@ struct ClockDisplayContainer: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.animation(UIConstants.AnimationCurves.smooth, value: isDisplayMode)
|
.animation(.smooth(duration: Design.Animation.standard), value: isDisplayMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// Container component that handles the positioning and display of top overlays
|
/// Container component that handles the positioning and display of top overlays
|
||||||
struct ClockOverlayContainer: View {
|
struct ClockOverlayContainer: View {
|
||||||
@ -20,12 +21,12 @@ struct ClockOverlayContainer: View {
|
|||||||
TopOverlayView(
|
TopOverlayView(
|
||||||
showBattery: style.showBattery,
|
showBattery: style.showBattery,
|
||||||
showDate: style.showDate,
|
showDate: style.showDate,
|
||||||
color: style.digitColor.opacity(UIConstants.Opacity.primary),
|
color: style.digitColor.opacity(Design.Opacity.heavy),
|
||||||
opacity: style.overlayOpacity,
|
opacity: style.overlayOpacity,
|
||||||
dateFormat: style.dateFormat
|
dateFormat: style.dateFormat
|
||||||
)
|
)
|
||||||
.padding(.top, UIConstants.Spacing.small)
|
.padding(.top, Design.Spacing.small)
|
||||||
.padding(.horizontal, UIConstants.Spacing.large)
|
.padding(.horizontal, Design.Spacing.large)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// Component for displaying segmented time with customizable formatting
|
/// Component for displaying segmented time with customizable formatting
|
||||||
struct TimeDisplayView: View {
|
struct TimeDisplayView: View {
|
||||||
@ -132,8 +133,8 @@ struct TimeDisplayView: View {
|
|||||||
}
|
}
|
||||||
.offset(y: portraitMode && forceHorizontalMode ? -containerSize.height * 0.10 : 0) // Push up in horizontal mode
|
.offset(y: portraitMode && forceHorizontalMode ? -containerSize.height * 0.10 : 0) // Push up in horizontal mode
|
||||||
.scaleEffect(finalScale, anchor: .center)
|
.scaleEffect(finalScale, anchor: .center)
|
||||||
.animation(UIConstants.AnimationCurves.smooth, value: finalScale)
|
.animation(.smooth(duration: Design.Animation.standard), value: finalScale)
|
||||||
.animation(UIConstants.AnimationCurves.smooth, value: showSeconds) // Smooth animation for seconds toggle
|
.animation(.smooth(duration: Design.Animation.standard), value: showSeconds) // Smooth animation for seconds toggle
|
||||||
.minimumScaleFactor(0.1)
|
.minimumScaleFactor(0.1)
|
||||||
.clipped() // Prevent overflow beyond bounds
|
.clipped() // Prevent overflow beyond bounds
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
/// Component for displaying top overlay with battery and date information
|
/// Component for displaying top overlay with battery and date information
|
||||||
struct TopOverlayView: View {
|
struct TopOverlayView: View {
|
||||||
@ -37,8 +38,8 @@ struct TopOverlayView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
.padding(.vertical, UIConstants.Spacing.small)
|
.padding(.vertical, Design.Spacing.small)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
batteryService.startMonitoring()
|
batteryService.startMonitoring()
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
import AudioPlaybackKit
|
import AudioPlaybackKit
|
||||||
|
|
||||||
/// Category-based sound selection view with grid layout
|
/// Category-based sound selection view with grid layout
|
||||||
@ -67,7 +68,7 @@ struct SoundCategoryView: View {
|
|||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: UIConstants.Spacing.medium) {
|
VStack(spacing: Design.Spacing.medium) {
|
||||||
// Search Bar
|
// Search Bar
|
||||||
searchBar
|
searchBar
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ struct SoundCategoryView: View {
|
|||||||
soundGrid
|
soundGrid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.top, UIConstants.Spacing.medium)
|
.padding(.top, Design.Spacing.medium)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Subviews
|
// MARK: - Subviews
|
||||||
@ -94,15 +95,15 @@ struct SoundCategoryView: View {
|
|||||||
.autocorrectionDisabled()
|
.autocorrectionDisabled()
|
||||||
.textInputAutocapitalization(.never)
|
.textInputAutocapitalization(.never)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
.padding(.vertical, UIConstants.Spacing.small)
|
.padding(.vertical, Design.Spacing.small)
|
||||||
.background(Color(.systemGray6))
|
.background(Color(.systemGray6))
|
||||||
.cornerRadius(10)
|
.cornerRadius(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var categoryTabs: some View {
|
private var categoryTabs: some View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
HStack(spacing: UIConstants.Spacing.small) {
|
HStack(spacing: Design.Spacing.small) {
|
||||||
ForEach(categories) { category in
|
ForEach(categories) { category in
|
||||||
CategoryTab(
|
CategoryTab(
|
||||||
title: category.displayName,
|
title: category.displayName,
|
||||||
@ -113,12 +114,12 @@ struct SoundCategoryView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var soundGrid: some View {
|
private var soundGrid: some View {
|
||||||
LazyVStack(spacing: UIConstants.Spacing.small) {
|
LazyVStack(spacing: Design.Spacing.small) {
|
||||||
ForEach(filteredSounds) { sound in
|
ForEach(filteredSounds) { sound in
|
||||||
SoundCard(
|
SoundCard(
|
||||||
sound: sound,
|
sound: sound,
|
||||||
@ -134,7 +135,7 @@ struct SoundCategoryView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,10 +158,10 @@ struct CategoryTab: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
.padding(.vertical, UIConstants.Spacing.small)
|
.padding(.vertical, Design.Spacing.small)
|
||||||
.background(isSelected ? UIConstants.Colors.accentColor : Color(.systemGray6))
|
.background(isSelected ? AppAccent.primary : Color(.systemGray6))
|
||||||
.foregroundColor(isSelected ? .white : UIConstants.Colors.primaryText)
|
.foregroundColor(isSelected ? .white : AppTextColors.primary)
|
||||||
.cornerRadius(20)
|
.cornerRadius(20)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
@ -175,20 +176,20 @@ struct SoundCard: View {
|
|||||||
let onPreview: () -> Void
|
let onPreview: () -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: UIConstants.Spacing.medium) {
|
HStack(spacing: Design.Spacing.medium) {
|
||||||
// Sound Icon (Left)
|
// Sound Icon (Left)
|
||||||
ZStack {
|
ZStack {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(isSelected ? UIConstants.Colors.accentColor : Color(.systemGray5))
|
.fill(isSelected ? AppAccent.primary : Color(.systemGray5))
|
||||||
.frame(width: 50, height: 50)
|
.frame(width: 50, height: 50)
|
||||||
|
|
||||||
Image(systemName: soundIcon)
|
Image(systemName: soundIcon)
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
.foregroundColor(isSelected ? .white : UIConstants.Colors.primaryText)
|
.foregroundColor(isSelected ? .white : AppTextColors.primary)
|
||||||
|
|
||||||
if isPreviewing {
|
if isPreviewing {
|
||||||
Circle()
|
Circle()
|
||||||
.stroke(UIConstants.Colors.accentColor, lineWidth: 2)
|
.stroke(AppAccent.primary, lineWidth: 2)
|
||||||
.frame(width: 58, height: 58)
|
.frame(width: 58, height: 58)
|
||||||
.scaleEffect(1.02)
|
.scaleEffect(1.02)
|
||||||
.animation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true), value: isPreviewing)
|
.animation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true), value: isPreviewing)
|
||||||
@ -200,7 +201,7 @@ struct SoundCard: View {
|
|||||||
// Sound Name
|
// Sound Name
|
||||||
Text(sound.name)
|
Text(sound.name)
|
||||||
.font(.subheadline.weight(.medium))
|
.font(.subheadline.weight(.medium))
|
||||||
.foregroundColor(UIConstants.Colors.primaryText)
|
.foregroundColor(AppTextColors.primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
||||||
// Description
|
// Description
|
||||||
@ -216,7 +217,7 @@ struct SoundCard: View {
|
|||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.horizontal, 6)
|
.padding(.horizontal, 6)
|
||||||
.padding(.vertical, 2)
|
.padding(.vertical, 2)
|
||||||
.background(UIConstants.Colors.accentColor, in: Capsule())
|
.background(AppAccent.primary, in: Capsule())
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
@ -224,14 +225,14 @@ struct SoundCard: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
.padding(.vertical, UIConstants.Spacing.small)
|
.padding(.vertical, Design.Spacing.small)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 12)
|
RoundedRectangle(cornerRadius: 12)
|
||||||
.fill(isSelected ? UIConstants.Colors.accentColor.opacity(0.1) : Color(.systemBackground))
|
.fill(isSelected ? AppAccent.primary.opacity(0.1) : Color(.systemBackground))
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 12)
|
RoundedRectangle(cornerRadius: 12)
|
||||||
.stroke(isSelected ? UIConstants.Colors.accentColor : Color.clear, lineWidth: 2)
|
.stroke(isSelected ? AppAccent.primary : Color.clear, lineWidth: 2)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
import AudioPlaybackKit
|
import AudioPlaybackKit
|
||||||
|
|
||||||
/// Component for audio playback controls
|
/// Component for audio playback controls
|
||||||
@ -19,18 +20,18 @@ struct SoundControlView: View {
|
|||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: UIConstants.Spacing.medium) {
|
VStack(spacing: Design.Spacing.medium) {
|
||||||
// Sound info header
|
// Sound info header
|
||||||
if let sound = selectedSound {
|
if let sound = selectedSound {
|
||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
Text(sound.name)
|
Text(sound.name)
|
||||||
.font(.headline.weight(.semibold))
|
.font(.headline.weight(.semibold))
|
||||||
.foregroundColor(UIConstants.Colors.primaryText)
|
.foregroundColor(AppTextColors.primary)
|
||||||
|
|
||||||
Text(sound.description)
|
Text(sound.description)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(UIConstants.Colors.secondaryText)
|
.foregroundColor(AppTextColors.secondary)
|
||||||
.lineLimit(2)
|
.lineLimit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,10 +43,10 @@ struct SoundControlView: View {
|
|||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.horizontal, 8)
|
.padding(.horizontal, 8)
|
||||||
.padding(.vertical, 4)
|
.padding(.vertical, 4)
|
||||||
.background(UIConstants.Colors.accentColor, in: Capsule())
|
.background(AppAccent.primary, in: Capsule())
|
||||||
}
|
}
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.medium, vertical: UIConstants.Spacing.small)
|
.contentPadding(horizontal: Design.Spacing.medium, vertical: Design.Spacing.small)
|
||||||
.background(UIConstants.Colors.overlayBackground, in: RoundedRectangle(cornerRadius: UIConstants.CornerRadius.medium))
|
.background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.large))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main control button
|
// Main control button
|
||||||
@ -56,7 +57,7 @@ struct SoundControlView: View {
|
|||||||
onPlay(sound)
|
onPlay(sound)
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
HStack(spacing: UIConstants.Spacing.small) {
|
HStack(spacing: Design.Spacing.small) {
|
||||||
Image(systemName: isPlaying ? "stop.fill" : "play.fill")
|
Image(systemName: isPlaying ? "stop.fill" : "play.fill")
|
||||||
.font(.title2.weight(.semibold))
|
.font(.title2.weight(.semibold))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
@ -65,11 +66,11 @@ struct SoundControlView: View {
|
|||||||
.font(.headline.weight(.semibold))
|
.font(.headline.weight(.semibold))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.medium)
|
.contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.medium)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large)
|
RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)
|
||||||
.fill(isPlaying ? Color.red : UIConstants.Colors.accentColor)
|
.fill(isPlaying ? Color.red : AppAccent.primary)
|
||||||
.shadow(color: (isPlaying ? Color.red : UIConstants.Colors.accentColor).opacity(0.3), radius: 8, x: 0, y: 4)
|
.shadow(color: (isPlaying ? Color.red : AppAccent.primary).opacity(0.3), radius: 8, x: 0, y: 4)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.disabled(selectedSound == nil)
|
.disabled(selectedSound == nil)
|
||||||
@ -79,11 +80,11 @@ struct SoundControlView: View {
|
|||||||
.animation(.easeInOut(duration: 0.2), value: selectedSound)
|
.animation(.easeInOut(duration: 0.2), value: selectedSound)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: 400) // Reasonable max width for iPad
|
.frame(maxWidth: 400) // Reasonable max width for iPad
|
||||||
.padding(UIConstants.Spacing.medium)
|
.padding(Design.Spacing.medium)
|
||||||
.background(UIConstants.Colors.overlayBackground, in: RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large))
|
.background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge))
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large)
|
RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)
|
||||||
.stroke(UIConstants.Colors.overlayBorder, lineWidth: UIConstants.BorderWidth.normal)
|
.stroke(AppBorder.subtle, lineWidth: Design.LineWidth.thin)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
import AudioPlaybackKit
|
import AudioPlaybackKit
|
||||||
|
|
||||||
/// Main noise/audio player view
|
/// Main noise/audio player view
|
||||||
@ -57,7 +58,7 @@ struct NoiseView: View {
|
|||||||
private var portraitLayout: some View {
|
private var portraitLayout: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
// Fixed header
|
// Fixed header
|
||||||
VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
Text("Ambient Sounds")
|
Text("Ambient Sounds")
|
||||||
.sectionTitleStyle()
|
.sectionTitleStyle()
|
||||||
|
|
||||||
@ -67,8 +68,8 @@ struct NoiseView: View {
|
|||||||
.centered()
|
.centered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large)
|
.contentPadding(horizontal: Design.Spacing.large)
|
||||||
.padding(.top, UIConstants.Spacing.large)
|
.padding(.top, Design.Spacing.large)
|
||||||
.background(Color(.systemBackground))
|
.background(Color(.systemBackground))
|
||||||
|
|
||||||
// Scrollable sound selection
|
// Scrollable sound selection
|
||||||
@ -77,15 +78,15 @@ struct NoiseView: View {
|
|||||||
sounds: viewModel.availableSounds,
|
sounds: viewModel.availableSounds,
|
||||||
selectedSound: $selectedSound
|
selectedSound: $selectedSound
|
||||||
)
|
)
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.large)
|
.contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var landscapeLayout: some View {
|
private var landscapeLayout: some View {
|
||||||
HStack(spacing: UIConstants.Spacing.large) {
|
HStack(spacing: Design.Spacing.large) {
|
||||||
// Left side: Player controls
|
// Left side: Player controls
|
||||||
VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
Text("Ambient Sounds")
|
Text("Ambient Sounds")
|
||||||
.sectionTitleStyle()
|
.sectionTitleStyle()
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ struct NoiseView: View {
|
|||||||
soundControlView
|
soundControlView
|
||||||
} else {
|
} else {
|
||||||
// Placeholder when no sound selected
|
// Placeholder when no sound selected
|
||||||
VStack(spacing: UIConstants.Spacing.small) {
|
VStack(spacing: Design.Spacing.small) {
|
||||||
Image(systemName: "music.note")
|
Image(systemName: "music.note")
|
||||||
.font(.largeTitle)
|
.font(.largeTitle)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
@ -103,14 +104,14 @@ struct NoiseView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.vertical, UIConstants.Spacing.large)
|
.padding(.vertical, Design.Spacing.large)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.frame(maxWidth: 400) // Reasonable width for player section
|
.frame(maxWidth: 400) // Reasonable width for player section
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large)
|
.contentPadding(horizontal: Design.Spacing.large)
|
||||||
.padding(.top, UIConstants.Spacing.large)
|
.padding(.top, Design.Spacing.large)
|
||||||
|
|
||||||
// Right side: Sound selection
|
// Right side: Sound selection
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@ -119,11 +120,11 @@ struct NoiseView: View {
|
|||||||
sounds: viewModel.availableSounds,
|
sounds: viewModel.availableSounds,
|
||||||
selectedSound: $selectedSound
|
selectedSound: $selectedSound
|
||||||
)
|
)
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.large, vertical: UIConstants.Spacing.large)
|
.contentPadding(horizontal: Design.Spacing.large, vertical: Design.Spacing.large)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.contentPadding(horizontal: UIConstants.Spacing.medium)
|
.contentPadding(horizontal: Design.Spacing.medium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,9 @@ import Bedrock
|
|||||||
|
|
||||||
extension Color {
|
extension Color {
|
||||||
enum Branding {
|
enum Branding {
|
||||||
static let primary = Color(red: 0.08, green: 0.10, blue: 0.16)
|
static let primary = AppSurface.primary
|
||||||
static let secondary = Color(red: 0.03, green: 0.05, blue: 0.10)
|
static let secondary = AppSurface.overlay
|
||||||
static let accent = Color.white
|
static let accent = AppTextColors.primary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
TheNoiseClock/Shared/Design/Design+NoiseClock.swift
Normal file
13
TheNoiseClock/Shared/Design/Design+NoiseClock.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Design+NoiseClock.swift
|
||||||
|
// TheNoiseClock
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 1/31/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
|
extension Design.CornerRadius {
|
||||||
|
static let appLarge: CGFloat = 16
|
||||||
|
}
|
||||||
@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// UIConstants+Animation.swift
|
|
||||||
// TheNoiseClock
|
|
||||||
//
|
|
||||||
// Created by Matt Bruce on 1/31/26.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import Bedrock
|
|
||||||
|
|
||||||
extension UIConstants {
|
|
||||||
|
|
||||||
// MARK: - Animation Curves
|
|
||||||
enum AnimationCurves {
|
|
||||||
static let smooth = Animation.smooth(duration: Design.Animation.standard)
|
|
||||||
static let bouncy = Animation.spring(duration: Design.Animation.springDuration, bounce: Design.Animation.springBounce)
|
|
||||||
static let quick = Animation.easeInOut(duration: Design.Animation.quick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// UIConstants+Colors.swift
|
|
||||||
// TheNoiseClock
|
|
||||||
//
|
|
||||||
// Created by Matt Bruce on 1/31/26.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
extension UIConstants {
|
|
||||||
|
|
||||||
// MARK: - Colors
|
|
||||||
enum Colors {
|
|
||||||
static let accentColor = AppAccent.primary
|
|
||||||
static let primaryText = AppTextColors.primary
|
|
||||||
static let secondaryText = AppTextColors.secondary
|
|
||||||
static let background = AppSurface.primary
|
|
||||||
static let overlayBackground = AppSurface.overlay
|
|
||||||
static let overlayBorder = AppBorder.subtle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
//
|
|
||||||
// UIConstants+Layout.swift
|
|
||||||
// TheNoiseClock
|
|
||||||
//
|
|
||||||
// Created by Matt Bruce on 1/31/26.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import Bedrock
|
|
||||||
|
|
||||||
extension UIConstants {
|
|
||||||
|
|
||||||
// MARK: - Spacing
|
|
||||||
enum Spacing {
|
|
||||||
static let extraSmall: CGFloat = Design.Spacing.xSmall
|
|
||||||
static let small: CGFloat = Design.Spacing.small
|
|
||||||
static let medium: CGFloat = Design.Spacing.medium
|
|
||||||
static let large: CGFloat = Design.Spacing.large
|
|
||||||
static let extraLarge: CGFloat = Design.Spacing.xLarge
|
|
||||||
static let huge: CGFloat = Design.Spacing.xxLarge
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Corner Radius
|
|
||||||
enum CornerRadius {
|
|
||||||
static let small: CGFloat = Design.CornerRadius.small
|
|
||||||
static let medium: CGFloat = Design.CornerRadius.large
|
|
||||||
static let large: CGFloat = 16
|
|
||||||
static let extraLarge: CGFloat = Design.CornerRadius.xxLarge
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Border Width
|
|
||||||
enum BorderWidth {
|
|
||||||
static let thin: CGFloat = Design.LineWidth.hairline
|
|
||||||
static let normal: CGFloat = Design.LineWidth.thin
|
|
||||||
static let thick: CGFloat = Design.LineWidth.standard
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Opacity
|
|
||||||
enum Opacity {
|
|
||||||
static let disabled: Double = Design.Opacity.light
|
|
||||||
static let secondary: Double = Design.Opacity.accent
|
|
||||||
static let primary: Double = Design.Opacity.heavy
|
|
||||||
static let full: Double = 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
//
|
|
||||||
// UIConstants.swift
|
|
||||||
// TheNoiseClock
|
|
||||||
//
|
|
||||||
// Created by Matt Bruce on 9/7/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
/// UI-specific constants for styling and layout.
|
|
||||||
enum UIConstants {}
|
|
||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Bedrock
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
|
|
||||||
@ -13,10 +14,10 @@ extension View {
|
|||||||
/// - Returns: View with card styling applied
|
/// - Returns: View with card styling applied
|
||||||
func cardStyle() -> some View {
|
func cardStyle() -> some View {
|
||||||
self
|
self
|
||||||
.background(UIConstants.Colors.overlayBackground, in: RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large))
|
.background(AppSurface.overlay, in: RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge))
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: UIConstants.CornerRadius.large)
|
RoundedRectangle(cornerRadius: Design.CornerRadius.appLarge)
|
||||||
.stroke(UIConstants.Colors.overlayBorder, lineWidth: UIConstants.BorderWidth.normal)
|
.stroke(AppBorder.subtle, lineWidth: Design.LineWidth.thin)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,12 +26,12 @@ extension View {
|
|||||||
/// - isEnabled: Whether the button is enabled
|
/// - isEnabled: Whether the button is enabled
|
||||||
/// - color: Button color
|
/// - color: Button color
|
||||||
/// - Returns: View with button styling applied
|
/// - Returns: View with button styling applied
|
||||||
func buttonStyle(isEnabled: Bool = true, color: Color = UIConstants.Colors.accentColor) -> some View {
|
func buttonStyle(isEnabled: Bool = true, color: Color = AppAccent.primary) -> some View {
|
||||||
self
|
self
|
||||||
.padding(UIConstants.Spacing.medium)
|
.padding(Design.Spacing.medium)
|
||||||
.background(isEnabled ? color : color.opacity(UIConstants.Opacity.disabled))
|
.background(isEnabled ? color : color.opacity(Design.Opacity.light))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.cornerRadius(UIConstants.CornerRadius.small)
|
.cornerRadius(Design.CornerRadius.small)
|
||||||
.disabled(!isEnabled)
|
.disabled(!isEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ extension View {
|
|||||||
func sectionTitleStyle() -> some View {
|
func sectionTitleStyle() -> some View {
|
||||||
self
|
self
|
||||||
.font(.title2.weight(.bold))
|
.font(.title2.weight(.bold))
|
||||||
.foregroundColor(UIConstants.Colors.primaryText)
|
.foregroundColor(AppTextColors.primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Center content horizontally with spacers
|
/// Center content horizontally with spacers
|
||||||
@ -84,7 +85,7 @@ extension View {
|
|||||||
/// - Parameter title: The title text
|
/// - Parameter title: The title text
|
||||||
/// - Returns: View with section header styling
|
/// - Returns: View with section header styling
|
||||||
func sectionHeader(title: String) -> some View {
|
func sectionHeader(title: String) -> some View {
|
||||||
VStack(alignment: .leading, spacing: UIConstants.Spacing.medium) {
|
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||||
Text(title)
|
Text(title)
|
||||||
.sectionTitleStyle()
|
.sectionTitleStyle()
|
||||||
self
|
self
|
||||||
@ -96,7 +97,7 @@ extension View {
|
|||||||
/// - horizontal: Horizontal padding amount
|
/// - horizontal: Horizontal padding amount
|
||||||
/// - vertical: Vertical padding amount
|
/// - vertical: Vertical padding amount
|
||||||
/// - Returns: View with standard padding applied
|
/// - Returns: View with standard padding applied
|
||||||
func contentPadding(horizontal: CGFloat = UIConstants.Spacing.large, vertical: CGFloat? = nil) -> some View {
|
func contentPadding(horizontal: CGFloat = Design.Spacing.large, vertical: CGFloat? = nil) -> some View {
|
||||||
self
|
self
|
||||||
.padding(.horizontal, horizontal)
|
.padding(.horizontal, horizontal)
|
||||||
.padding(.vertical, vertical ?? horizontal)
|
.padding(.vertical, vertical ?? horizontal)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user