#43: Add the DesignKit package to the project.

* Add ElementTextFieldStyle and static style properties.
* Rename button styles, support sizes and simplify colour.
This commit is contained in:
Doug 2022-06-09 14:44:46 +01:00 committed by GitHub
parent 7a458377d6
commit 596cf2df0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 366 additions and 163 deletions

View File

@ -17,32 +17,44 @@
import SwiftUI
import DesignTokens
public struct PrimaryActionButtonStyle: ButtonStyle {
public extension ButtonStyle where Self == ElementActionButtonStyle {
/// The CTA button style as defined in Compound.
/// - Parameter size: The control size to use. Defaults to regular.
/// - Parameter color: The color of the button's background. Defaults to the accent color.
static func elementAction(_ size: ElementControlSize = .regular,
color: Color = .element.accent) -> ElementActionButtonStyle {
ElementActionButtonStyle(size: size, color: color)
}
}
public struct ElementActionButtonStyle: ButtonStyle {
@Environment(\.isEnabled) private var isEnabled
@Environment(\.colorScheme) private var colorScheme
public var customColor: Color?
public var size: ElementControlSize
public var color: Color
private var verticalPadding: CGFloat { size == .xLarge ? 12 : 4 }
private var maxWidth: CGFloat? { size == .xLarge ? .infinity : nil }
private var fontColor: Color {
// Always white unless disabled with a dark theme.
.white.opacity(colorScheme == .dark && !isEnabled ? 0.3 : 1.0)
}
private var backgroundColor: Color {
customColor ?? .element.accent
}
public init(customColor: Color? = nil) {
self.customColor = customColor
public init(size: ElementControlSize = .regular, color: Color = .element.accent) {
self.size = size
self.color = color
}
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.padding(12.0)
.frame(maxWidth: .infinity)
.padding(.horizontal, 12)
.padding(.vertical, verticalPadding)
.frame(maxWidth: maxWidth)
.foregroundColor(fontColor)
.font(.element.body)
.background(backgroundColor.opacity(backgroundOpacity(when: configuration.isPressed)))
.background(color.opacity(backgroundOpacity(when: configuration.isPressed)))
.cornerRadius(8.0)
}
@ -52,24 +64,24 @@ public struct PrimaryActionButtonStyle: ButtonStyle {
}
}
public struct PrimaryActionButtonStyle_Previews: PreviewProvider {
public struct ElementActionButtonStyle_Previews: PreviewProvider {
public static var states: some View {
VStack {
Button("Enabled") { /* preview */ }
.buttonStyle(PrimaryActionButtonStyle())
.buttonStyle(ElementActionButtonStyle())
Button("Disabled") { /* preview */ }
.buttonStyle(PrimaryActionButtonStyle())
.buttonStyle(ElementActionButtonStyle())
.disabled(true)
Button { /* preview */ } label: {
Text("Clear BG")
.foregroundColor(.element.alert)
}
.buttonStyle(PrimaryActionButtonStyle(customColor: .clear))
.buttonStyle(ElementActionButtonStyle(color: .clear))
Button("Red BG") { /* preview */ }
.buttonStyle(PrimaryActionButtonStyle(customColor: .element.alert))
.buttonStyle(ElementActionButtonStyle(color: .element.alert))
}
.padding()
}

View File

@ -0,0 +1,99 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
import DesignTokens
public extension ButtonStyle where Self == ElementGhostButtonStyle {
/// The Ghost button style as defined in Compound.
/// - Parameter size: The control size to use. Defaults to `regular`.
/// - Parameter color: The color of the label and border. Defaults to the accent color.
static func elementGhost(_ size: ElementControlSize = .regular,
color: Color = .element.accent) -> ElementGhostButtonStyle {
ElementGhostButtonStyle(size: size, color: color)
}
}
public struct ElementGhostButtonStyle: ButtonStyle {
@Environment(\.isEnabled) private var isEnabled
public var size: ElementControlSize
public var color: Color
private var verticalPadding: CGFloat { size == .xLarge ? 12 : 4 }
private var maxWidth: CGFloat? { size == .xLarge ? .infinity : nil }
public init(size: ElementControlSize = .regular, color: Color = .element.accent) {
self.size = size
self.color = color
}
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.padding(.horizontal, 12)
.padding(.vertical, verticalPadding)
.frame(maxWidth: maxWidth)
.foregroundColor(color)
.font(.element.body)
.background(border)
.opacity(opacity(when: configuration.isPressed))
}
private var border: some View {
RoundedRectangle(cornerRadius: 8)
.strokeBorder()
.foregroundColor(color)
}
private func opacity(when isPressed: Bool) -> CGFloat {
guard isEnabled else { return 0.6 }
return isPressed ? 0.6 : 1.0
}
}
public struct ElementGhostButtonStyle_Previews: PreviewProvider {
public static var previews: some View {
Group {
states
}
.preferredColorScheme(.light)
Group {
states
}
.preferredColorScheme(.dark)
}
public static var states: some View {
VStack {
Button("Enabled") { /* preview */ }
.buttonStyle(ElementGhostButtonStyle())
Button("Disabled") { /* preview */ }
.buttonStyle(ElementGhostButtonStyle())
.disabled(true)
Button("Red BG") { /* preview */ }
.buttonStyle(ElementGhostButtonStyle(color: .element.alert))
Button { /* preview */ } label: {
Text("Custom")
.foregroundColor(.element.primaryContent)
}
.buttonStyle(ElementGhostButtonStyle(color: .element.quaternaryContent))
}
.padding()
}
}

View File

@ -1,82 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
import DesignTokens
public struct SecondaryActionButtonStyle: ButtonStyle {
@Environment(\.isEnabled) private var isEnabled
public var customColor: Color?
public init(customColor: Color? = nil) {
self.customColor = customColor
}
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.padding(12.0)
.frame(maxWidth: .infinity)
.foregroundColor(strokeColor(configuration.isPressed))
.font(.element.body)
.background(RoundedRectangle(cornerRadius: 8)
.strokeBorder()
.foregroundColor(strokeColor(configuration.isPressed)))
.opacity(isEnabled ? 1.0 : 0.6)
}
private func strokeColor(_ isPressed: Bool) -> Color {
if let customColor = customColor {
return customColor
}
return isPressed ? .element.accent.opacity(0.6) : .element.accent
}
}
public struct SecondaryActionButtonStyle_Previews: PreviewProvider {
public static var previews: some View {
Group {
states
}
.preferredColorScheme(.light)
Group {
states
}
.preferredColorScheme(.dark)
}
public static var states: some View {
VStack {
Button("Enabled") { /* preview */ }
.buttonStyle(SecondaryActionButtonStyle())
Button("Disabled") { /* preview */ }
.buttonStyle(SecondaryActionButtonStyle())
.disabled(true)
Button { /* preview */ } label: {
Text("Clear BG")
.foregroundColor(.element.alert)
}
.buttonStyle(SecondaryActionButtonStyle(customColor: .clear))
Button("Red BG") { /* preview */ }
.buttonStyle(SecondaryActionButtonStyle(customColor: .element.alert))
}
.padding()
}
}

View File

@ -0,0 +1,22 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
public enum ElementControlSize {
case regular
case xLarge
}

View File

@ -16,12 +16,16 @@
import SwiftUI
struct RoundedCornerShape: Shape {
public struct RoundedCornerShape: Shape {
let radius: CGFloat
let corners: UIRectCorner
func path(in rect: CGRect) -> Path {
public init(radius: CGFloat, corners: UIRectCorner) {
self.radius = radius
self.corners = corners
}
public func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}

View File

@ -28,22 +28,15 @@ public struct BorderedInputFieldStyle: TextFieldStyle {
public var isEditing: Bool
public var isError: Bool
public var returnKey: UIReturnKeyType?
private var borderColor: Color {
if isError {
return .element.alert
} else if isEditing {
return .element.accent
} else {
return .element.quinaryContent
}
guard !isError else { return .element.alert }
return isEditing ? .element.accent : .element.quinaryContent
}
private var accentColor: Color {
if isError {
return .element.alert
}
return .element.accent
isError ? .element.alert : .element.accent
}
private var textColor: Color {
@ -62,16 +55,22 @@ public struct BorderedInputFieldStyle: TextFieldStyle {
}
private var placeholderColor: Color {
return .element.tertiaryContent
.element.tertiaryContent
}
private var borderWidth: CGFloat {
return isEditing || isError ? 2.0 : 1.5
isEditing || isError ? 2.0 : 1.5
}
public init(isEditing: Bool = false, isError: Bool = false) {
/// Creates the text field style configured as required.
/// - Parameters:
/// - isEditing: Whether or not the text field is currently being edited.
/// - isError: Whether or not the text field is currently in the error state.
/// - returnKey: The return key to be used. Pass `nil` for iOS 15+ and use `.submitLabel` instead.
public init(isEditing: Bool = false, isError: Bool = false, returnKey: UIReturnKeyType? = .done) {
self.isEditing = isEditing
self.isError = isError
self.returnKey = returnKey
}
public func _body(configuration: TextField<_Label>) -> some View {
@ -80,13 +79,16 @@ public struct BorderedInputFieldStyle: TextFieldStyle {
.font(.element.callout)
.foregroundColor(textColor)
.accentColor(accentColor)
.frame(height: 48.0)
.padding(.vertical, 12.0)
.padding(.horizontal, 8.0)
.background(backgroundColor)
.clipShape(rect)
.overlay(rect.stroke(borderColor, lineWidth: borderWidth))
.introspectTextField { textField in
textField.returnKeyType = .done
if let returnKey = returnKey {
textField.returnKeyType = returnKey
}
textField.clearButtonMode = .whileEditing
textField.attributedPlaceholder = NSAttributedString(string: textField.placeholder ?? "",
attributes: [NSAttributedString.Key.foregroundColor: UIColor(placeholderColor)])

View File

@ -0,0 +1,100 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
import DesignTokens
@available(iOS 15.0, *)
public extension TextFieldStyle where Self == ElementTextFieldStyle {
static func elementInput(labelText: String? = nil,
footerText: String? = nil,
isError: Bool = false) -> ElementTextFieldStyle {
ElementTextFieldStyle(labelText: labelText, footerText: footerText, isError: isError)
}
}
@available(iOS 15.0, *)
public struct ElementTextFieldStyle: TextFieldStyle {
@Environment(\.isEnabled) private var isEnabled
@Environment(\.colorScheme) private var colorScheme
@FocusState private var isFocused: Bool
public let labelText: String?
public let footerText: String?
public let isError: Bool
private var labelColor: Color {
guard colorScheme == .light else { return .element.tertiaryContent }
return isEnabled ? .element.primaryContent : .element.quaternaryContent
}
private var footerColor: Color {
isError ? .element.alert : .element.tertiaryContent
}
public init(labelText: String? = nil, footerText: String? = nil, isError: Bool = false) {
self.labelText = labelText
self.footerText = footerText
self.isError = isError
}
public func _body(configuration: TextField<_Label>) -> some View {
VStack(alignment: .leading, spacing: 8) {
if let labelText = labelText {
Text(labelText)
.font(.element.subheadline)
.foregroundColor(labelColor)
}
configuration
.textFieldStyle(BorderedInputFieldStyle(isEditing: isFocused, isError: isError, returnKey: nil))
.focused($isFocused)
if let footerText = footerText {
Text(footerText)
.font(.element.footnote)
.foregroundColor(footerColor)
}
}
}
}
@available(iOS 15.0, *)
struct ElementTextFieldStyle_Previews: PreviewProvider {
public static var states: some View {
VStack(spacing: 12) {
TextField("Placeholder", text: .constant(""))
.textFieldStyle(.elementInput(labelText: "Label", footerText: "Footer"))
TextField("Placeholder", text: .constant("Input text"))
.textFieldStyle(.elementInput(labelText: "Title", footerText: "Footer"))
TextField("Placeholder", text: .constant("Bad text"))
.textFieldStyle(.elementInput(labelText: "Title", footerText: "Footer", isError: true))
TextField("Placeholder", text: .constant(""))
.textFieldStyle(.elementInput(labelText: "Title", footerText: "Footer"))
.disabled(true)
}
.padding()
}
public static var previews: some View {
Group {
states
.preferredColorScheme(.light)
states
.preferredColorScheme(.dark)
}
}
}

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
@ -38,7 +38,7 @@
1999ECC6777752A2616775CF /* MemberDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A152791A2F56BD193BFE986 /* MemberDetailsProvider.swift */; };
1A70A2199394B5EC660934A5 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = A678E40E917620059695F067 /* MatrixRustSDK */; };
1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */; };
1F3232BD368DF430AB433907 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 531CE4334AC5CA8DFF6AEB84 /* DTCoreText */; };
1F3232BD368DF430AB433907 /* DesignKit in Frameworks */ = {isa = PBXBuildFile; productRef = A5A56C4F47C368EBE5C5E870 /* DesignKit */; };
206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; };
224A55EEAEECF5336B14A4A5 /* EmoteRoomMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2DF459F1737A594667CC46 /* EmoteRoomMessage.swift */; };
22DADD537401E79D66132134 /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4173A48FD8542CD4AD3645C /* NavigationRouter.swift */; };
@ -71,11 +71,12 @@
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; };
39AE84C8E5F2FE9D2DC7775C /* EventBasedTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56008790A9C4479A6B31FDF4 /* EventBasedTimelineView.swift */; };
3B770CB4DED51CC362C66D47 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4990FDBDA96B88E214F92F48 /* SettingsModels.swift */; };
3C549A0BF39F8A854D45D9FD /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 7731767AE437BA3BD2CC14A8 /* Sentry */; };
3C549A0BF39F8A854D45D9FD /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 997C7385E1A07E061D7E2100 /* GZIP */; };
3D325A1147F6281C57BFCDF6 /* EventBrief.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4411C0DA0087A1CB143E96FA /* EventBrief.swift */; };
3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC38E64A5ED3FDB201029A /* BugReportService.swift */; };
3ED2725734568F6B8CC87544 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */; };
418B4AEFD03DC7A6D2C9D5C8 /* EventBriefFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36322DD0D4E29D31B0945ADC /* EventBriefFactory.swift */; };
41DFDD212D1BE57CA50D783B /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 7731767AE437BA3BD2CC14A8 /* Sentry */; };
438FB9BC535BC95948AA5F34 /* SettingsViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2F9D5C39A4494D19F33E38 /* SettingsViewModelProtocol.swift */; };
462813B93C39DF93B1249403 /* RoundedToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFABDF2E19D349DAAAC18C65 /* RoundedToastView.swift */; };
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; };
@ -111,7 +112,7 @@
6A367F3D7A437A79B7D9A31C /* FullscreenLoadingViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4112D04077F6709C5CA0A13E /* FullscreenLoadingViewPresenter.swift */; };
6C72F66DA26A0956E9A9077A /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BEB3259B2208E5AE5BB3F65 /* Settings.swift */; };
6EA61FCA55D950BDE326A1A7 /* ImageAnonymizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */; };
6F2AB43A1EFAD8A97AF41A15 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 5986E300FC849DEAB2EE7AEB /* Introspect */; };
6F2AB43A1EFAD8A97AF41A15 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0DD568A494247444A4B56031 /* Kingfisher */; };
7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */; };
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0CBC76C80E04345E11F2DB /* Application.swift */; };
7434A7F02D587A920B376A9A /* LoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */; };
@ -141,18 +142,18 @@
8D9F646387DF656EF91EE4CB /* RoomMessageFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F37AB24AF5A006521D38D1 /* RoomMessageFactoryProtocol.swift */; };
90DF83A6A347F7EE7EDE89EE /* AttributedStringBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */; };
90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
93BA4A81B6D893271101F9F0 /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = FD43A50D9B75C9D6D30F006B /* SwiftyBeaver */; };
93BA4A81B6D893271101F9F0 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 5986E300FC849DEAB2EE7AEB /* Introspect */; };
964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B902EA6CD3296B0E10EE432B /* HomeScreen.swift */; };
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; };
992F5E750F5030C4BA2D0D03 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01C4C7DB37597D7D8379511A /* Assets.xcassets */; };
99ED42B8F8D6BFB1DBCF4C45 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; };
9AC5F8142413862A9E3A2D98 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 9573B94B1C86C6DF751AF3FD /* SwiftState */; };
9AC5F8142413862A9E3A2D98 /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = FD43A50D9B75C9D6D30F006B /* SwiftyBeaver */; };
9B8DE1D424E37581C7D99CCC /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC7CCC6DE5FA623E31BA8546 /* RoomTimelineControllerProtocol.swift */; };
9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */; };
9C45CE85325CD591DADBC4CA /* ElementXTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */; };
9C9E48A627C7C166084E3F5B /* LabelledActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F01DD1BBD4450E18115916 /* LabelledActivityIndicatorView.swift */; };
9CB5129C83F75921E5E28028 /* ToastViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C82DAE0B8EB28234E84E6CF /* ToastViewState.swift */; };
9D2E03DB175A6AB14589076D /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0DD568A494247444A4B56031 /* Kingfisher */; };
9D2E03DB175A6AB14589076D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; };
9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6E4C37E9F0E53D3DF951AC /* HomeScreenUITests.swift */; };
A0A0D2A9564BDA3FDE2E360F /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */; };
A32517FB1CA0BBCE2BC75249 /* BugReportCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6C07DA7D3FF193F7419F55 /* BugReportCoordinator.swift */; };
@ -183,7 +184,7 @@
C4F69156C31A447FEFF2A47C /* DTHTMLElement+AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E508AB0EDEE017FF4F6F8D1 /* DTHTMLElement+AttributedStringBuilder.swift */; };
C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */; };
C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */; };
CB137BFB3E083C33E398A6CB /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 997C7385E1A07E061D7E2100 /* GZIP */; };
CB137BFB3E083C33E398A6CB /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 9573B94B1C86C6DF751AF3FD /* SwiftState */; };
CB326BAB54E9B68658909E36 /* Benchmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */; };
CB498F4E27AA0545DCEF0F6F /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 50009897F60FAE7D63EF5E5B /* Kingfisher */; };
CC736DA1AA8F8B9FD8785009 /* ScreenshotDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C4AF6E3885730CD560311C /* ScreenshotDetector.swift */; };
@ -207,7 +208,7 @@
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
F03E16ED043C62FED5A07AE0 /* MatrixEntitityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B81C8227BBEA95CCE86037 /* MatrixEntitityRegex.swift */; };
F56261126E368C831B3DE976 /* NavigationRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752DEC02D93AFF46BC13313A /* NavigationRouterType.swift */; };
F656F92A63D3DC1978D79427 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; };
F656F92A63D3DC1978D79427 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 531CE4334AC5CA8DFF6AEB84 /* DTCoreText */; };
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
F78C57B197DA74735FEBB42C /* EventBriefFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B61C243325DC76D3086494 /* EventBriefFactoryProtocol.swift */; };
FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; };
@ -509,6 +510,7 @@
CF47564C584F614B7287F3EB /* RootRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootRouter.swift; sourceTree = "<group>"; };
D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineView.swift; sourceTree = "<group>"; };
D29EBCBFEC6FD0941749404D /* NavigationRouterStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouterStore.swift; sourceTree = "<group>"; };
D31DC8105C6233E5FFD9B84C /* element-x-ios */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "element-x-ios"; path = .; sourceTree = SOURCE_ROOT; };
D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
D67CBAFA48ED0B6FCE74F88F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -578,14 +580,15 @@
buildActionMask = 2147483647;
files = (
1A70A2199394B5EC660934A5 /* MatrixRustSDK in Frameworks */,
1F3232BD368DF430AB433907 /* DTCoreText in Frameworks */,
F656F92A63D3DC1978D79427 /* KeychainAccess in Frameworks */,
9D2E03DB175A6AB14589076D /* Kingfisher in Frameworks */,
6F2AB43A1EFAD8A97AF41A15 /* Introspect in Frameworks */,
93BA4A81B6D893271101F9F0 /* SwiftyBeaver in Frameworks */,
9AC5F8142413862A9E3A2D98 /* SwiftState in Frameworks */,
CB137BFB3E083C33E398A6CB /* GZIP in Frameworks */,
3C549A0BF39F8A854D45D9FD /* Sentry in Frameworks */,
1F3232BD368DF430AB433907 /* DesignKit in Frameworks */,
F656F92A63D3DC1978D79427 /* DTCoreText in Frameworks */,
9D2E03DB175A6AB14589076D /* KeychainAccess in Frameworks */,
6F2AB43A1EFAD8A97AF41A15 /* Kingfisher in Frameworks */,
93BA4A81B6D893271101F9F0 /* Introspect in Frameworks */,
9AC5F8142413862A9E3A2D98 /* SwiftyBeaver in Frameworks */,
CB137BFB3E083C33E398A6CB /* SwiftState in Frameworks */,
3C549A0BF39F8A854D45D9FD /* GZIP in Frameworks */,
41DFDD212D1BE57CA50D783B /* Sentry in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -742,6 +745,7 @@
A4852B57D55D71EEBFCD931D /* UnitTests */,
C0FAC17D4DD7D3A502822550 /* UITests */,
823ED0EC3F1B6CF47D284011 /* Tools */,
9413F680ECDFB2B0DDB0DEF2 /* Packages */,
681566846AF307E9BA4C72C6 /* Products */,
);
sourceTree = "<group>";
@ -1016,6 +1020,14 @@
path = HTMLParsing;
sourceTree = "<group>";
};
9413F680ECDFB2B0DDB0DEF2 /* Packages */ = {
isa = PBXGroup;
children = (
D31DC8105C6233E5FFD9B84C /* element-x-ios */,
);
name = Packages;
sourceTree = SOURCE_ROOT;
};
951A66D15CD44C0EACE4A951 /* Sources */ = {
isa = PBXGroup;
children = (
@ -1336,6 +1348,7 @@
name = ElementX;
packageProductDependencies = (
A678E40E917620059695F067 /* MatrixRustSDK */,
A5A56C4F47C368EBE5C5E870 /* DesignKit */,
531CE4334AC5CA8DFF6AEB84 /* DTCoreText */,
020597E28A4BC8E1BE8EDF6E /* KeychainAccess */,
0DD568A494247444A4B56031 /* Kingfisher */,
@ -1367,7 +1380,7 @@
};
};
buildConfigurationList = 7AE41FCCF9D1352E2770D1F9 /* Build configuration list for PBXProject "ElementX" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@ -2360,6 +2373,10 @@
package = 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */;
productName = GZIP;
};
A5A56C4F47C368EBE5C5E870 /* DesignKit */ = {
isa = XCSwiftPackageProductDependency;
productName = DesignKit;
};
A678E40E917620059695F067 /* MatrixRustSDK */ = {
isa = XCSwiftPackageProductDependency;
package = 4FCDA8D25C7415C8FB33490D /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */;

View File

@ -18,6 +18,15 @@
"version" : "1.7.18"
}
},
{
"identity" : "element-design-tokens",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/element-design-tokens.git",
"state" : {
"branch" : "main",
"revision" : "4aafdc25ca0e322c0de930d4ec86121f5503023e"
}
},
{
"identity" : "gzip",
"kind" : "remoteSourceControl",
@ -59,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/getsentry/sentry-cocoa.git",
"state" : {
"revision" : "afa4cd596e5cf97a797fb9b5c3afeea4add2c7a3",
"version" : "7.15.0"
"revision" : "b34ce9048e6b8b082ea6b15e6b50583d46c936ea",
"version" : "7.16.0"
}
},
{

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"

View File

@ -26,6 +26,7 @@ enum LoginScreenViewAction {
struct LoginScreenViewState: BindableState {
var bindings: LoginScreenViewStateBindings
var hasCredentials: Bool { !bindings.username.isEmpty && !bindings.password.isEmpty }
}
struct LoginScreenViewStateBindings {

View File

@ -15,35 +15,48 @@
//
import SwiftUI
import DesignKit
struct LoginScreen: View {
@ObservedObject var context: LoginScreenViewModel.Context
enum Field { case username, password }
@FocusState private var focussedField: Field?
var body: some View {
NavigationView {
VStack {
TextField("Username", text: $context.username)
.textFieldStyle(.roundedBorder)
.disableAutocorrection(true)
.autocapitalization(.none)
SecureField("Password", text: $context.password)
.textFieldStyle(.roundedBorder)
Button("Login") {
context.send(viewAction: .login)
}
.buttonStyle(.borderedProminent)
.padding(.horizontal, 50)
}
.padding(.horizontal, 8.0)
.navigationTitle("Login")
.navigationBarTitleDisplayMode(.inline)
.onSubmit {
context.send(viewAction: .login)
}
VStack {
TextField("Username", text: $context.username)
.textFieldStyle(.elementInput())
.disableAutocorrection(true)
.textContentType(.username)
.autocapitalization(.none)
.focused($focussedField, equals: .username)
.submitLabel(.next)
.onSubmit { focussedField = .password }
SecureField("Password", text: $context.password)
.textFieldStyle(.elementInput())
.textContentType(.password)
.focused($focussedField, equals: .password)
.submitLabel(.go)
.onSubmit(submit)
Button("Login", action: submit)
.buttonStyle(.elementAction(.xLarge))
.disabled(!context.viewState.hasCredentials)
}
.navigationViewStyle(StackNavigationViewStyle())
.padding(.horizontal, 8.0)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.element.background.ignoresSafeArea())
.navigationTitle("Login")
.navigationBarTitleDisplayMode(.inline)
}
func submit() {
guard context.viewState.hasCredentials else { return }
context.send(viewAction: .login)
focussedField = nil
}
}
@ -52,6 +65,9 @@ struct LoginScreen: View {
struct LoginScreen_Previews: PreviewProvider {
static var previews: some View {
let viewModel = LoginScreenViewModel()
LoginScreen(context: viewModel.context)
NavigationView {
LoginScreen(context: viewModel.context)
}
.navigationViewStyle(.stack)
}
}

View File

@ -82,6 +82,7 @@ targets:
dependencies:
- package: MatrixRustSDK
- package: DesignKit
- package: DTCoreText
- package: KeychainAccess
- package: Kingfisher

View File

@ -21,7 +21,6 @@ class LoginScreenUITests: XCTestCase {
let app = Application.launch()
app.goToScreenWithIdentifier("Login screen")
XCTAssert(app.staticTexts["Login"].exists)
XCTAssert(app.buttons["Login"].exists)
XCTAssert(app.textFields["Username"].exists)
XCTAssert(app.secureTextFields["Password"].exists)

1
changelog.d/43.change Normal file
View File

@ -0,0 +1 @@
DesignKit: Add DesignKit to the ElementX project and style the login screen with it.

View File

@ -33,6 +33,8 @@ packages:
MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift.git
branch: main
DesignKit:
path: ./
DTCoreText:
url: https://github.com/Cocoanetics/DTCoreText
majorVersion: 1.6.26