mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
#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:
parent
7a458377d6
commit
596cf2df0b
@ -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()
|
||||
}
|
99
DesignKit/Sources/Buttons/ElementGhostButtonStyle.swift
Normal file
99
DesignKit/Sources/Buttons/ElementGhostButtonStyle.swift
Normal 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()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
22
DesignKit/Sources/Common/ElementControlSize.swift
Normal file
22
DesignKit/Sources/Common/ElementControlSize.swift
Normal 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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)])
|
||||
|
100
DesignKit/Sources/TextFields/ElementTextFieldStyle.swift
Normal file
100
DesignKit/Sources/TextFields/ElementTextFieldStyle.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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" */;
|
||||
|
@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -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"
|
||||
|
@ -26,6 +26,7 @@ enum LoginScreenViewAction {
|
||||
|
||||
struct LoginScreenViewState: BindableState {
|
||||
var bindings: LoginScreenViewStateBindings
|
||||
var hasCredentials: Bool { !bindings.username.isEmpty && !bindings.password.isEmpty }
|
||||
}
|
||||
|
||||
struct LoginScreenViewStateBindings {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ targets:
|
||||
|
||||
dependencies:
|
||||
- package: MatrixRustSDK
|
||||
- package: DesignKit
|
||||
- package: DTCoreText
|
||||
- package: KeychainAccess
|
||||
- package: Kingfisher
|
||||
|
@ -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
1
changelog.d/43.change
Normal file
@ -0,0 +1 @@
|
||||
DesignKit: Add DesignKit to the ElementX project and style the login screen with it.
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user