diff --git a/Plugin/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift b/Plugin/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift
index e3bac5be..fae1762d 100644
--- a/Plugin/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift
+++ b/Plugin/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift
@@ -10,6 +10,7 @@ public extension TargetDependency {
}
public extension TargetDependency.Project.Features {
+ static let SigninFeature = TargetDependency.feature(name: "SigninFeature")
static let BaseFeature = TargetDependency.feature(name: "BaseFeature")
static let RootFeature = TargetDependency.feature(name: "RootFeature")
}
diff --git a/Projects/App/Sources/Application/DI/AppComponent.swift b/Projects/App/Sources/Application/DI/AppComponent.swift
index 7c90feb1..432802c2 100644
--- a/Projects/App/Sources/Application/DI/AppComponent.swift
+++ b/Projects/App/Sources/Application/DI/AppComponent.swift
@@ -1,6 +1,7 @@
import NeedleFoundation
import SwiftUI
import KeychainModule
+import SigninFeature
public final class AppComponent: BootstrapComponent {
public func makeRootView() -> some View {
@@ -11,3 +12,9 @@ public final class AppComponent: BootstrapComponent {
KeychainImpl()
}
}
+
+public extension AppComponent {
+ var signinComponent: SigninComponent {
+ SigninComponent(parent: self)
+ }
+}
diff --git a/Projects/App/Sources/Application/DMSApp.swift b/Projects/App/Sources/Application/DMSApp.swift
index a151d9ae..462dbda2 100644
--- a/Projects/App/Sources/Application/DMSApp.swift
+++ b/Projects/App/Sources/Application/DMSApp.swift
@@ -1,11 +1,18 @@
import SwiftUI
import DesignSystem
+import SigninFeature
@main
struct DMSApp: App {
+ init() {
+ registerProviderFactories()
+ }
+
var body: some Scene {
WindowGroup {
- DesignSystemPlaygroundView()
+ NavigationView {
+ AppComponent().signinComponent.makeView()
+ }
}
}
}
diff --git a/Projects/App/Sources/Application/NeedleGenerated.swift b/Projects/App/Sources/Application/NeedleGenerated.swift
index af5d4a11..fd2007e1 100644
--- a/Projects/App/Sources/Application/NeedleGenerated.swift
+++ b/Projects/App/Sources/Application/NeedleGenerated.swift
@@ -5,6 +5,7 @@ import DomainModule
import KeychainModule
import NeedleFoundation
import NetworkModule
+import SigninFeature
import SwiftUI
// swiftlint:disable unused_declaration
@@ -20,11 +21,46 @@ private func parent1(_ component: NeedleFoundation.Scope) -> NeedleFoundation.Sc
#if !NEEDLE_DYNAMIC
+private class SigninDependencyde06a9d0b22764487733Provider: SigninDependency {
+ var signinUseCase: any SigninUseCase {
+ return appComponent.signinUseCase
+ }
+ private let appComponent: AppComponent
+ init(appComponent: AppComponent) {
+ self.appComponent = appComponent
+ }
+}
+/// ^->AppComponent->SigninComponent
+private func factory2882a056d84a613debccf47b58f8f304c97af4d5(_ component: NeedleFoundation.Scope) -> AnyObject {
+ return SigninDependencyde06a9d0b22764487733Provider(appComponent: parent1(component) as! AppComponent)
+}
#else
extension AppComponent: Registration {
public func registerItems() {
+ localTable["keychain-any Keychain"] = { self.keychain as Any }
+ localTable["remoteAuthDataSource-any RemoteAuthDataSource"] = { self.remoteAuthDataSource as Any }
+ localTable["authRepository-any AuthRepository"] = { self.authRepository as Any }
+ localTable["signinUseCase-any SigninUseCase"] = { self.signinUseCase as Any }
+ localTable["verifyAuthCodeUseCase-any VerifyAuthCodeUseCase"] = { self.verifyAuthCodeUseCase as Any }
+ localTable["sendAuthCodeUseCase-any SendAuthCodeUseCase"] = { self.sendAuthCodeUseCase as Any }
+ localTable["checkEmailExistByAccountIDUseCase-any CheckEmailExistByAccountIDUseCase"] = { self.checkEmailExistByAccountIDUseCase as Any }
+ localTable["checkAccountIDIsExistUseCase-any CheckAccountIDIsExistUseCase"] = { self.checkAccountIDIsExistUseCase as Any }
+ localTable["signinComponent-SigninComponent"] = { self.signinComponent as Any }
+ localTable["remoteStudentsDataSource-any RemoteStudentsDataSource"] = { self.remoteStudentsDataSource as Any }
+ localTable["studentsRepository-any StudentsRepository"] = { self.studentsRepository as Any }
+ localTable["signupUseCase-any SignupUseCase"] = { self.signupUseCase as Any }
+ localTable["checkDuplicateAccountIDUseCase-any CheckDuplicateAccountIDUseCase"] = { self.checkDuplicateAccountIDUseCase as Any }
+ localTable["checkDuplicateEmailUseCase-any CheckDuplicateEmailUseCase"] = { self.checkDuplicateEmailUseCase as Any }
+ localTable["renewalPasswordUseCase-any RenewalPasswordUseCase"] = { self.renewalPasswordUseCase as Any }
+ localTable["findIDUseCase-any FindIDUseCase"] = { self.findIDUseCase as Any }
+ localTable["fetchMyProfileUseCase-any FetchMyProfileUseCase"] = { self.fetchMyProfileUseCase as Any }
+ }
+}
+extension SigninComponent: Registration {
+ public func registerItems() {
+ keyPathToName[\SigninDependency.signinUseCase] = "signinUseCase-any SigninUseCase"
}
}
@@ -44,6 +80,7 @@ private func registerProviderFactory(_ componentPath: String, _ factory: @escapi
private func register1() {
registerProviderFactory("^->AppComponent", factoryEmptyDependencyProvider)
+ registerProviderFactory("^->AppComponent->SigninComponent", factory2882a056d84a613debccf47b58f8f304c97af4d5)
}
#endif
diff --git a/Projects/Features/RootFeature/Project.swift b/Projects/Features/RootFeature/Project.swift
index 88cafc68..4da1352b 100644
--- a/Projects/Features/RootFeature/Project.swift
+++ b/Projects/Features/RootFeature/Project.swift
@@ -5,6 +5,7 @@ let project = Project.makeModule(
name: "RootFeature",
product: .staticFramework,
dependencies: [
- .Project.Features.BaseFeature
+ .Project.Features.BaseFeature,
+ .Project.Features.SigninFeature
]
)
diff --git a/Projects/Features/SigninFeature/Derived/InfoPlists/SigninFeature-Info.plist b/Projects/Features/SigninFeature/Derived/InfoPlists/SigninFeature-Info.plist
new file mode 100644
index 00000000..323e5ecf
--- /dev/null
+++ b/Projects/Features/SigninFeature/Derived/InfoPlists/SigninFeature-Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+
diff --git a/Projects/Features/SigninFeature/Derived/InfoPlists/SigninFeatureTests-Info.plist b/Projects/Features/SigninFeature/Derived/InfoPlists/SigninFeatureTests-Info.plist
new file mode 100644
index 00000000..6c40a6cd
--- /dev/null
+++ b/Projects/Features/SigninFeature/Derived/InfoPlists/SigninFeatureTests-Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+
diff --git a/Projects/Features/SigninFeature/Project.swift b/Projects/Features/SigninFeature/Project.swift
new file mode 100644
index 00000000..564617bd
--- /dev/null
+++ b/Projects/Features/SigninFeature/Project.swift
@@ -0,0 +1,10 @@
+import ProjectDescription
+import ProjectDescriptionHelpers
+
+let project = Project.makeModule(
+ name: "SigninFeature",
+ product: .staticFramework,
+ dependencies: [
+ .Project.Features.BaseFeature
+ ]
+)
diff --git a/Projects/Features/SigninFeature/Sources/SigninComponent.swift b/Projects/Features/SigninFeature/Sources/SigninComponent.swift
new file mode 100644
index 00000000..f64cdfed
--- /dev/null
+++ b/Projects/Features/SigninFeature/Sources/SigninComponent.swift
@@ -0,0 +1,17 @@
+import DomainModule
+import NeedleFoundation
+import SwiftUI
+
+public protocol SigninDependency: Dependency {
+ var signinUseCase: any SigninUseCase { get }
+}
+
+public final class SigninComponent: Component {
+ public func makeView() -> some View {
+ SigninView(
+ viewModel: .init(
+ signinUseCase: dependency.signinUseCase
+ )
+ )
+ }
+}
diff --git a/Projects/Features/SigninFeature/Sources/SigninView.swift b/Projects/Features/SigninFeature/Sources/SigninView.swift
new file mode 100644
index 00000000..b678d3ca
--- /dev/null
+++ b/Projects/Features/SigninFeature/Sources/SigninView.swift
@@ -0,0 +1,117 @@
+import SwiftUI
+import DesignSystem
+
+struct SigninView: View {
+ private enum FocusField {
+ case id
+ case password
+ }
+ @StateObject var viewModel: SigninViewModel
+ @FocusState private var focusField: FocusField?
+
+ public init(viewModel: SigninViewModel) {
+ _viewModel = StateObject(wrappedValue: viewModel)
+ }
+
+ var body: some View {
+ VStack {
+ HStack {
+ VStack(alignment: .leading, spacing: 8) {
+ Text("DMS")
+ .dmsFont(.title(.extraLarge), color: .PrimaryVariant.primary)
+ .padding(.top, 28)
+
+ Text("더 편한 기숙사 생활을 위해")
+ .dmsFont(.text(.medium), color: .GrayScale.gray6)
+ }
+
+ Spacer()
+ }
+
+ VStack(spacing: 72) {
+ DMSFloatingTextField(
+ "아이디",
+ text: $viewModel.id,
+ isError: viewModel.isErrorOcuured
+ ) {
+ focusField = .password
+ }
+ .textContentType(.username)
+ .focused($focusField, equals: .id)
+
+ SecureDMSFloatingTextField("비밀번호", text: $viewModel.password) {
+ viewModel.signinButtonDidTap()
+ }
+ .textContentType(.password)
+ .focused($focusField, equals: .password)
+ }
+ .padding(.top, 68)
+
+ HStack(spacing: 16) {
+ HStack(spacing: 12) {
+ DMSRadioButton(isOn: $viewModel.isOnAutoSignin)
+
+ Text("자동로그인")
+ .dmsFont(.text(.small), color: .GrayScale.gray6)
+ }
+ .onTapGesture {
+ withAnimation {
+ viewModel.isOnAutoSignin.toggle()
+ }
+ }
+
+ Spacer()
+
+ NavigationLink {
+ Text("아이디 찾기")
+ } label: {
+ Text("아이디 찾기")
+ .dmsFont(.text(.extraSmall), color: .GrayScale.gray5)
+ }
+
+ Divider()
+ .foregroundColor(.GrayScale.gray5)
+ .frame(height: 13)
+
+ NavigationLink {
+ Text("비밀번호 재설정")
+ } label: {
+ Text("비밀번호 재설정")
+ .dmsFont(.text(.extraSmall), color: .GrayScale.gray5)
+ }
+ }
+ .padding(.top, 16)
+
+ Spacer()
+
+ HStack(spacing: 16) {
+ Text("아직 회원이 아니신가요?")
+ .dmsFont(.text(.extraSmall), color: .GrayScale.gray5)
+
+ DMSButton(text: "회원가입", style: .text, color: .GrayScale.gray6) {
+ viewModel.isNavigateSignup.toggle()
+ }
+ }
+
+ DMSWideButton(text: "로그인", color: .PrimaryVariant.primary) {
+ viewModel.signinButtonDidTap()
+ }
+ .disabled(!viewModel.isSigninButtonEnabled)
+ .padding(.top, 24)
+ .frame(maxWidth: .infinity)
+ .padding(.bottom, 40)
+ }
+ .dmsToast(isShowing: $viewModel.isErrorOcuured, message: viewModel.errorMessage, style: .error)
+ .frame(maxWidth: .infinity)
+ .padding(.horizontal, 24)
+ .dmsBackground()
+ .navigate(to: Text("회원가입"), when: $viewModel.isNavigateSignup)
+ .ignoresSafeArea(.keyboard, edges: .bottom)
+ }
+}
+
+struct SigninView_Previews: PreviewProvider {
+ static var previews: some View {
+ Text("A")
+ }
+}
diff --git a/Projects/Features/SigninFeature/Sources/SigninViewModel.swift b/Projects/Features/SigninFeature/Sources/SigninViewModel.swift
new file mode 100644
index 00000000..502240d1
--- /dev/null
+++ b/Projects/Features/SigninFeature/Sources/SigninViewModel.swift
@@ -0,0 +1,27 @@
+import BaseFeature
+import Combine
+import DomainModule
+
+final class SigninViewModel: BaseViewModel {
+ @Published var id = ""
+ @Published var password = ""
+ @Published var isOnAutoSignin = true
+ @Published var isNavigateSignup = false
+ @Published var isSuccessSignin = false
+ var isSigninButtonEnabled: Bool {
+ !id.isEmpty && !password.isEmpty
+ }
+
+ private let signinUseCase: any SigninUseCase
+
+ public init(signinUseCase: any SigninUseCase) {
+ self.signinUseCase = signinUseCase
+ }
+
+ func signinButtonDidTap() {
+ guard isSigninButtonEnabled else { return }
+ addCancellable(signinUseCase.execute(req: .init(accountID: id, password: password))) { [weak self] _ in
+ self?.isSuccessSignin = true
+ }
+ }
+}
diff --git a/Projects/Features/SigninFeature/Tests/SigninViewModelSpec.swift b/Projects/Features/SigninFeature/Tests/SigninViewModelSpec.swift
new file mode 100644
index 00000000..83709c66
--- /dev/null
+++ b/Projects/Features/SigninFeature/Tests/SigninViewModelSpec.swift
@@ -0,0 +1,85 @@
+import Quick
+import Nimble
+import Combine
+import DomainModule
+import DataModule
+@testable import SigninFeature
+
+// swiftlint: disable function_body_length
+final class SigninViewModelSpec: QuickSpec {
+ override func spec() {
+ var signinUseCase: SigninUseCase!
+ var sut: SigninViewModel!
+
+ beforeEach {
+ signinUseCase = SigninUseCaseFake()
+ sut = SigninViewModel(signinUseCase: signinUseCase)
+ }
+ describe("SigninViewModel에서") {
+ context("유저가 아무것도 입력하지 않은 상태라면") {
+ it("isSigninButtonEnabled은 false이다.") {
+ expect { sut.id }.to(beEmpty())
+ expect { sut.password }.to(beEmpty())
+ expect { sut.isSigninButtonEnabled }.to(beFalse())
+ }
+ }
+ context("유저가 ID만 입력했다면") {
+ beforeEach {
+ sut.id = "A"
+ }
+ it("isSigninButtonEnabled은 false이다.") {
+ expect { sut.id }.toNot(beEmpty())
+ expect { sut.password }.to(beEmpty())
+ expect { sut.isSigninButtonEnabled }.to(beFalse())
+ }
+ }
+ context("유저가 Password만 입력했다면") {
+ beforeEach {
+ sut.password = "A"
+ }
+ it("isSigninButtonEnabled은 false이다.") {
+ expect { sut.id }.to(beEmpty())
+ expect { sut.password }.toNot(beEmpty())
+ expect { sut.isSigninButtonEnabled }.to(beFalse())
+ }
+ }
+ context("유저가 ID와 Password를 모두 입력했다면") {
+ beforeEach {
+ sut.id = "A"
+ sut.password = "A"
+ }
+ it("isSigninButtonEnabled은 true이다.") {
+ expect { sut.id }.toNot(beEmpty())
+ expect { sut.password }.toNot(beEmpty())
+ expect { sut.isSigninButtonEnabled }.to(beTrue())
+ }
+ }
+ context("유저가 ID와 Password를 알맞게 입력하고 로그인을 시도한다면") {
+ beforeEach {
+ sut.id = "baekteun"
+ sut.password = "baekteun"
+ sut.signinButtonDidTap()
+ }
+ it("isSuccessSignin이 true이다.") {
+ expect { sut.isSuccessSignin }.toEventually(beTrue())
+ }
+ }
+ context("유저가 ID와 Password중 하나라도 알맞지 않게 입력하고 로그인을 시도한다면") {
+ beforeEach {
+ sut.id = "ASDF"
+ sut.password = "ASDF"
+ sut.signinButtonDidTap()
+ }
+ it("isSuccessSignin은 false이다") {
+ expect { sut.isSuccessSignin }.toEventually(beFalse())
+ }
+ it("isErrorOcuured는 true이다") {
+ expect { sut.isErrorOcuured }.toEventually(beTrue())
+ }
+ it("errorMessage는 '아이디 또는 비밀번호를 확인해주세요.'가 나온다") {
+ expect { sut.errorMessage }.toEventually(equal("아이디 또는 비밀번호를 확인해주세요."))
+ }
+ }
+ }
+ }
+}
diff --git a/Projects/Services/DataModule/Sources/Auth/UseCases/Fake/SigninUseCaseFake.swift b/Projects/Services/DataModule/Sources/Auth/UseCases/Fake/SigninUseCaseFake.swift
new file mode 100644
index 00000000..da4db55a
--- /dev/null
+++ b/Projects/Services/DataModule/Sources/Auth/UseCases/Fake/SigninUseCaseFake.swift
@@ -0,0 +1,21 @@
+import Combine
+import DataMappingModule
+import DomainModule
+import ErrorModule
+import Foundation
+
+public struct SigninUseCaseFake: SigninUseCase {
+ public init () {}
+
+ public func execute(req: SigninRequestDTO) -> AnyPublisher {
+ if req.accountID == "baekteun" && req.password == "baekteun" {
+ return Just(()).setFailureType(to: DmsError.self)
+ .delay(for: 1, scheduler: DispatchQueue.main)
+ .eraseToAnyPublisher()
+ } else {
+ return Fail(error: DmsError.passwordMismatch)
+ .delay(for: 1, scheduler: DispatchQueue.main)
+ .eraseToAnyPublisher()
+ }
+ }
+}
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 1.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 1.colorset/Contents.json
index 22c4bb0a..04256378 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 1.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 1.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "1.000",
- "green" : "1.000",
- "red" : "1.000"
+ "blue" : "0.000",
+ "green" : "0.000",
+ "red" : "0.000"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 2.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 2.colorset/Contents.json
index 6b0a608f..4db45498 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 2.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 2.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.976",
- "green" : "0.976",
- "red" : "0.976"
+ "blue" : "0.071",
+ "green" : "0.071",
+ "red" : "0.071"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 3.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 3.colorset/Contents.json
index db155404..c95d2c62 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 3.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 3.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.933",
- "green" : "0.933",
- "red" : "0.933"
+ "blue" : "0.204",
+ "green" : "0.204",
+ "red" : "0.204"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 4.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 4.colorset/Contents.json
index f504201b..79d164b4 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 4.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 4.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.867",
- "green" : "0.867",
- "red" : "0.867"
+ "blue" : "0.333",
+ "green" : "0.333",
+ "red" : "0.333"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 6.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 6.colorset/Contents.json
index 47a7c537..f4efbefa 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 6.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 6.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.333",
- "green" : "0.333",
- "red" : "0.333"
+ "blue" : "0.867",
+ "green" : "0.867",
+ "red" : "0.867"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 7.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 7.colorset/Contents.json
index 3f3264b0..818c0309 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 7.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 7.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.204",
- "green" : "0.204",
- "red" : "0.204"
+ "blue" : "0.933",
+ "green" : "0.933",
+ "red" : "0.933"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 8.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 8.colorset/Contents.json
index a04f4b42..40840fdd 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 8.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 8.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.071",
- "green" : "0.071",
- "red" : "0.071"
+ "blue" : "0.976",
+ "green" : "0.976",
+ "red" : "0.976"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 9.colorset/Contents.json b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 9.colorset/Contents.json
index 784f6038..d8907191 100644
--- a/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 9.colorset/Contents.json
+++ b/Projects/UsertInterfaces/DesignSystem/Resources/PrimaryColor.xcassets/Gray 9.colorset/Contents.json
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.000",
- "green" : "0.000",
- "red" : "0.000"
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
}
},
"idiom" : "universal"
diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/Extensions/View+navigate.swift b/Projects/UsertInterfaces/DesignSystem/Sources/Extensions/View+navigate.swift
new file mode 100644
index 00000000..4a91d416
--- /dev/null
+++ b/Projects/UsertInterfaces/DesignSystem/Sources/Extensions/View+navigate.swift
@@ -0,0 +1,34 @@
+import SwiftUI
+
+public extension View {
+ func navigate(
+ to view: NewView,
+ when binding: Binding,
+ isDetailLink: Bool = false
+ ) -> some View {
+ self.background {
+ NavigationLink(isActive: binding) {
+ DeferView {
+ view
+ }
+ } label: {
+ EmptyView()
+ }
+ .isDetailLink(isDetailLink)
+ }
+ }
+}
+
+public struct DeferView: View {
+ let content: () -> Content
+
+ public init(
+ @ViewBuilder _ content: @escaping () -> Content
+ ) {
+ self.content = content
+ }
+
+ public var body: some View {
+ content()
+ }
+}
diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/DMSFloatingTextField.swift b/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/DMSFloatingTextField.swift
index c60b882a..2b6c5137 100644
--- a/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/DMSFloatingTextField.swift
+++ b/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/DMSFloatingTextField.swift
@@ -8,6 +8,7 @@ public struct DMSFloatingTextField: View {
var errorMessage: String
var onCommit: () -> Void
@FocusState var isFocused: Bool
+ @Namespace var animation
private var isFloaintg: Bool {
isFocused || !text.isEmpty
}
@@ -41,8 +42,9 @@ public struct DMSFloatingTextField: View {
public var body: some View {
ZStack(alignment: .leading) {
Text(label)
- .dmsFont(.text(isFloaintg ? .medium : .extraLarge), color: dmsForegroundColor)
+ .dmsFont(.text(.extraLarge), color: dmsForegroundColor)
.offset(y: isFloaintg ? -40 : isErrorOrHelpNotEmpty ? -10 : 0)
+ .scaleEffect(isFloaintg ? 0.8 : 1, anchor: .topLeading)
VStack(alignment: .leading, spacing: 10) {
TextField("", text: $text)
@@ -57,9 +59,13 @@ public struct DMSFloatingTextField: View {
.focused($isFocused)
.onSubmit(onCommit)
- if isErrorOrHelpNotEmpty {
- Text(isError ? errorMessage : helpMessage)
- .dmsFont(.text(.extraSmall), color: isError ? .System.error : .GrayScale.gray5)
+ if !isError && !helpMessage.isEmpty {
+ Text(helpMessage)
+ .dmsFont(.text(.extraSmall), color: .GrayScale.gray5)
+ .offset(x: 5)
+ } else if isError && !errorMessage.isEmpty {
+ Text(errorMessage)
+ .dmsFont(.text(.extraSmall), color: .System.error)
.offset(x: 5)
}
}
diff --git a/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/SecureDMSFloatingTextField.swift b/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/SecureDMSFloatingTextField.swift
index fc083b9d..d1c1d05a 100644
--- a/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/SecureDMSFloatingTextField.swift
+++ b/Projects/UsertInterfaces/DesignSystem/Sources/TextField/Floating/SecureDMSFloatingTextField.swift
@@ -43,8 +43,9 @@ public struct SecureDMSFloatingTextField: View {
ZStack(alignment: .leading) {
HStack {
Text(label)
- .dmsFont(.text(isFloaintg ? .medium : .extraLarge), color: dmsForegroundColor)
+ .dmsFont(.text(.extraLarge), color: dmsForegroundColor)
.offset(y: isFloaintg ? -40 : isErrorOrHelpNotEmpty ? -10 : 0)
+ .scaleEffect(isFloaintg ? 0.8 : 1, anchor: .topLeading)
.onTapGesture {
isFocused = true
}