mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
#43: Add DesignKit package
* Begin implementing DesignKit package. * Use element-design-tokens repo. * Rename Fonts to align with Colours.
This commit is contained in:
parent
8fec97217f
commit
edf765b7cd
9
.gitignore
vendored
9
.gitignore
vendored
@ -9,6 +9,15 @@ xcuserdata/
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## SwiftPM
|
||||
/.build
|
||||
/Packages
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/config/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
|
83
DesignKit/Sources/Buttons/PrimaryActionButtonStyle.swift
Normal file
83
DesignKit/Sources/Buttons/PrimaryActionButtonStyle.swift
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// 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 PrimaryActionButtonStyle: ButtonStyle {
|
||||
@Environment(\.isEnabled) private var isEnabled
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
public var customColor: Color?
|
||||
|
||||
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 func makeBody(configuration: Self.Configuration) -> some View {
|
||||
configuration.label
|
||||
.padding(12.0)
|
||||
.frame(maxWidth: .infinity)
|
||||
.foregroundColor(fontColor)
|
||||
.font(.element.body)
|
||||
.background(backgroundColor.opacity(backgroundOpacity(when: configuration.isPressed)))
|
||||
.cornerRadius(8.0)
|
||||
}
|
||||
|
||||
private func backgroundOpacity(when isPressed: Bool) -> CGFloat {
|
||||
guard isEnabled else { return 0.3 }
|
||||
return isPressed ? 0.6 : 1.0
|
||||
}
|
||||
}
|
||||
|
||||
public struct PrimaryActionButtonStyle_Previews: PreviewProvider {
|
||||
public static var states: some View {
|
||||
VStack {
|
||||
Button("Enabled") { /* preview */ }
|
||||
.buttonStyle(PrimaryActionButtonStyle())
|
||||
|
||||
Button("Disabled") { /* preview */ }
|
||||
.buttonStyle(PrimaryActionButtonStyle())
|
||||
.disabled(true)
|
||||
|
||||
Button { /* preview */ } label: {
|
||||
Text("Clear BG")
|
||||
.foregroundColor(.element.alert)
|
||||
}
|
||||
.buttonStyle(PrimaryActionButtonStyle(customColor: .clear))
|
||||
|
||||
Button("Red BG") { /* preview */ }
|
||||
.buttonStyle(PrimaryActionButtonStyle(customColor: .element.alert))
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
public static var previews: some View {
|
||||
states
|
||||
.preferredColorScheme(.light)
|
||||
states
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
82
DesignKit/Sources/Buttons/SecondaryActionButtonStyle.swift
Normal file
82
DesignKit/Sources/Buttons/SecondaryActionButtonStyle.swift
Normal file
@ -0,0 +1,82 @@
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
}
|
69
DesignKit/Sources/Fonts/ElementFonts.swift
Normal file
69
DesignKit/Sources/Fonts/ElementFonts.swift
Normal file
@ -0,0 +1,69 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
public extension Font {
|
||||
/// The fonts used by Element as defined in https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0
|
||||
static let element = ElementFonts(values: ElementSharedFonts())
|
||||
}
|
||||
|
||||
/// Struct for holding fonts for use in SwiftUI.
|
||||
public struct ElementFonts: Fonts {
|
||||
public let largeTitle: Font
|
||||
public let largeTitleB: Font
|
||||
public let title1: Font
|
||||
public let title1B: Font
|
||||
public let title2: Font
|
||||
public let title2B: Font
|
||||
public let title3: Font
|
||||
public let title3SB: Font
|
||||
public let headline: Font
|
||||
public let subheadline: Font
|
||||
public let body: Font
|
||||
public let bodySB: Font
|
||||
public let callout: Font
|
||||
public let calloutSB: Font
|
||||
public let footnote: Font
|
||||
public let footnoteSB: Font
|
||||
public let caption1: Font
|
||||
public let caption1SB: Font
|
||||
public let caption2: Font
|
||||
public let caption2SB: Font
|
||||
|
||||
public init(values: ElementSharedFonts) {
|
||||
largeTitle = values.largeTitle.font
|
||||
largeTitleB = values.largeTitleB.font
|
||||
title1 = values.title1.font
|
||||
title1B = values.title1B.font
|
||||
title2 = values.title2.font
|
||||
title2B = values.title2B.font
|
||||
title3 = values.title3.font
|
||||
title3SB = values.title3SB.font
|
||||
headline = values.headline.font
|
||||
subheadline = values.subheadline.font
|
||||
body = values.body.font
|
||||
bodySB = values.bodySB.font
|
||||
callout = values.callout.font
|
||||
calloutSB = values.calloutSB.font
|
||||
footnote = values.footnote.font
|
||||
footnoteSB = values.footnoteSB.font
|
||||
caption1 = values.caption1.font
|
||||
caption1SB = values.caption1SB.font
|
||||
caption2 = values.caption2.font
|
||||
caption2SB = values.caption2SB.font
|
||||
}
|
||||
}
|
145
DesignKit/Sources/Fonts/ElementSharedFonts.swift
Normal file
145
DesignKit/Sources/Fonts/ElementSharedFonts.swift
Normal file
@ -0,0 +1,145 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// Fonts at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0
|
||||
@objcMembers
|
||||
public class ElementSharedFonts {
|
||||
|
||||
// MARK: - Types
|
||||
|
||||
/// A wrapper to provide both a `UIFont` and a SwiftUI `Font` in the same type.
|
||||
/// The need for this comes from `Font` not adapting for dynamic type until the app
|
||||
/// is restarted (or working at all in Xcode Previews) when initialised from a `UIFont`
|
||||
/// (even if that font was created with the appropriate metrics).
|
||||
public struct SharedFont {
|
||||
public let uiFont: UIFont
|
||||
public let font: Font
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Returns an instance of the font associated with the text style and scaled appropriately for the content size category defined in the trait collection.
|
||||
/// Keep this method private method at the moment and create a DesignKit.Fonts.TextStyle if needed.
|
||||
fileprivate func font(forTextStyle textStyle: UIFont.TextStyle, compatibleWith traitCollection: UITraitCollection? = nil) -> UIFont {
|
||||
return UIFont.preferredFont(forTextStyle: textStyle, compatibleWith: traitCollection)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Fonts protocol
|
||||
extension ElementSharedFonts {
|
||||
|
||||
public var largeTitle: SharedFont {
|
||||
let uiFont = font(forTextStyle: .largeTitle)
|
||||
return SharedFont(uiFont: uiFont, font: .largeTitle)
|
||||
}
|
||||
|
||||
public var largeTitleB: SharedFont {
|
||||
let uiFont = largeTitle.uiFont.bold
|
||||
return SharedFont(uiFont: uiFont, font: .largeTitle.bold())
|
||||
}
|
||||
|
||||
public var title1: SharedFont {
|
||||
let uiFont = font(forTextStyle: .title1)
|
||||
return SharedFont(uiFont: uiFont, font: .title)
|
||||
}
|
||||
|
||||
public var title1B: SharedFont {
|
||||
let uiFont = title1.uiFont.bold
|
||||
return SharedFont(uiFont: uiFont, font: .title.bold())
|
||||
}
|
||||
|
||||
public var title2: SharedFont {
|
||||
let uiFont = font(forTextStyle: .title2)
|
||||
return SharedFont(uiFont: uiFont, font: .title2)
|
||||
}
|
||||
|
||||
public var title2B: SharedFont {
|
||||
let uiFont = title2.uiFont.bold
|
||||
return SharedFont(uiFont: uiFont, font: .title2.bold())
|
||||
}
|
||||
|
||||
public var title3: SharedFont {
|
||||
let uiFont = font(forTextStyle: .title3)
|
||||
return SharedFont(uiFont: uiFont, font: .title3)
|
||||
}
|
||||
|
||||
public var title3SB: SharedFont {
|
||||
let uiFont = title3.uiFont.semiBold
|
||||
return SharedFont(uiFont: uiFont, font: .title3.weight(.semibold))
|
||||
}
|
||||
|
||||
public var headline: SharedFont {
|
||||
let uiFont = font(forTextStyle: .headline)
|
||||
return SharedFont(uiFont: uiFont, font: .headline)
|
||||
}
|
||||
|
||||
public var subheadline: SharedFont {
|
||||
let uiFont = font(forTextStyle: .subheadline)
|
||||
return SharedFont(uiFont: uiFont, font: .subheadline)
|
||||
}
|
||||
|
||||
public var body: SharedFont {
|
||||
let uiFont = font(forTextStyle: .body)
|
||||
return SharedFont(uiFont: uiFont, font: .body)
|
||||
}
|
||||
|
||||
public var bodySB: SharedFont {
|
||||
let uiFont = body.uiFont.semiBold
|
||||
return SharedFont(uiFont: uiFont, font: .body.weight(.semibold))
|
||||
}
|
||||
|
||||
public var callout: SharedFont {
|
||||
let uiFont = font(forTextStyle: .callout)
|
||||
return SharedFont(uiFont: uiFont, font: .callout)
|
||||
}
|
||||
|
||||
public var calloutSB: SharedFont {
|
||||
let uiFont = callout.uiFont.semiBold
|
||||
return SharedFont(uiFont: uiFont, font: .callout.weight(.semibold))
|
||||
}
|
||||
|
||||
public var footnote: SharedFont {
|
||||
let uiFont = font(forTextStyle: .footnote)
|
||||
return SharedFont(uiFont: uiFont, font: .footnote)
|
||||
}
|
||||
|
||||
public var footnoteSB: SharedFont {
|
||||
let uiFont = footnote.uiFont.semiBold
|
||||
return SharedFont(uiFont: uiFont, font: .footnote.weight(.semibold))
|
||||
}
|
||||
|
||||
public var caption1: SharedFont {
|
||||
let uiFont = font(forTextStyle: .caption1)
|
||||
return SharedFont(uiFont: uiFont, font: .caption)
|
||||
}
|
||||
|
||||
public var caption1SB: SharedFont {
|
||||
let uiFont = caption1.uiFont.semiBold
|
||||
return SharedFont(uiFont: uiFont, font: .caption.weight(.semibold))
|
||||
}
|
||||
|
||||
public var caption2: SharedFont {
|
||||
let uiFont = font(forTextStyle: .caption2)
|
||||
return SharedFont(uiFont: uiFont, font: .caption2)
|
||||
}
|
||||
|
||||
public var caption2SB: SharedFont {
|
||||
let uiFont = caption2.uiFont.semiBold
|
||||
return SharedFont(uiFont: uiFont, font: .caption2.weight(.semibold))
|
||||
}
|
||||
}
|
69
DesignKit/Sources/Fonts/ElementUIFonts.swift
Normal file
69
DesignKit/Sources/Fonts/ElementUIFonts.swift
Normal file
@ -0,0 +1,69 @@
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
public extension UIFont {
|
||||
/// The fonts used by Element as defined in https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0
|
||||
@objc static let element = ElementUIFonts(values: ElementSharedFonts())
|
||||
}
|
||||
|
||||
/// ObjC class for holding fonts for use in UIKit.
|
||||
@objcMembers public class ElementUIFonts: NSObject, Fonts {
|
||||
public let largeTitle: UIFont
|
||||
public let largeTitleB: UIFont
|
||||
public let title1: UIFont
|
||||
public let title1B: UIFont
|
||||
public let title2: UIFont
|
||||
public let title2B: UIFont
|
||||
public let title3: UIFont
|
||||
public let title3SB: UIFont
|
||||
public let headline: UIFont
|
||||
public let subheadline: UIFont
|
||||
public let body: UIFont
|
||||
public let bodySB: UIFont
|
||||
public let callout: UIFont
|
||||
public let calloutSB: UIFont
|
||||
public let footnote: UIFont
|
||||
public let footnoteSB: UIFont
|
||||
public let caption1: UIFont
|
||||
public let caption1SB: UIFont
|
||||
public let caption2: UIFont
|
||||
public let caption2SB: UIFont
|
||||
|
||||
public init(values: ElementSharedFonts) {
|
||||
largeTitle = values.largeTitle.uiFont
|
||||
largeTitleB = values.largeTitleB.uiFont
|
||||
title1 = values.title1.uiFont
|
||||
title1B = values.title1B.uiFont
|
||||
title2 = values.title2.uiFont
|
||||
title2B = values.title2B.uiFont
|
||||
title3 = values.title3.uiFont
|
||||
title3SB = values.title3SB.uiFont
|
||||
headline = values.headline.uiFont
|
||||
subheadline = values.subheadline.uiFont
|
||||
body = values.body.uiFont
|
||||
bodySB = values.bodySB.uiFont
|
||||
callout = values.callout.uiFont
|
||||
calloutSB = values.calloutSB.uiFont
|
||||
footnote = values.footnote.uiFont
|
||||
footnoteSB = values.footnoteSB.uiFont
|
||||
caption1 = values.caption1.uiFont
|
||||
caption1SB = values.caption1SB.uiFont
|
||||
caption2 = values.caption2.uiFont
|
||||
caption2SB = values.caption2SB.uiFont
|
||||
}
|
||||
}
|
85
DesignKit/Sources/Fonts/Fonts.swift
Normal file
85
DesignKit/Sources/Fonts/Fonts.swift
Normal file
@ -0,0 +1,85 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// Describe fonts used in the application.
|
||||
/// Font names are based on Element typography https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0 which is based on Apple font text styles (UIFont.TextStyle): https://developer.apple.com/documentation/uikit/uifonttextstyle
|
||||
/// Create a custom TextStyle enum (like DesignKit.Fonts.TextStyle) is also a possibility
|
||||
public protocol Fonts {
|
||||
|
||||
associatedtype FontType
|
||||
|
||||
/// The font for large titles.
|
||||
var largeTitle: FontType { get }
|
||||
|
||||
/// `largeTitle` with a Bold weight.
|
||||
var largeTitleB: FontType { get }
|
||||
|
||||
/// The font for first-level hierarchical headings.
|
||||
var title1: FontType { get }
|
||||
|
||||
/// `title1` with a Bold weight.
|
||||
var title1B: FontType { get }
|
||||
|
||||
/// The font for second-level hierarchical headings.
|
||||
var title2: FontType { get }
|
||||
|
||||
/// `title2` with a Bold weight.
|
||||
var title2B: FontType { get }
|
||||
|
||||
/// The font for third-level hierarchical headings.
|
||||
var title3: FontType { get }
|
||||
|
||||
/// `title3` with a Semi Bold weight.
|
||||
var title3SB: FontType { get }
|
||||
|
||||
/// The font for headings.
|
||||
var headline: FontType { get }
|
||||
|
||||
/// The font for subheadings.
|
||||
var subheadline: FontType { get }
|
||||
|
||||
/// The font for body text.
|
||||
var body: FontType { get }
|
||||
|
||||
/// `body` with a Semi Bold weight.
|
||||
var bodySB: FontType { get }
|
||||
|
||||
/// The font for callouts.
|
||||
var callout: FontType { get }
|
||||
|
||||
/// `callout` with a Semi Bold weight.
|
||||
var calloutSB: FontType { get }
|
||||
|
||||
/// The font for footnotes.
|
||||
var footnote: FontType { get }
|
||||
|
||||
/// `footnote` with a Semi Bold weight.
|
||||
var footnoteSB: FontType { get }
|
||||
|
||||
/// The font for standard captions.
|
||||
var caption1: FontType { get }
|
||||
|
||||
/// `caption1` with a Semi Bold weight.
|
||||
var caption1SB: FontType { get }
|
||||
|
||||
/// The font for alternate captions.
|
||||
var caption2: FontType { get }
|
||||
|
||||
/// `caption2` with a Semi Bold weight.
|
||||
var caption2SB: FontType { get }
|
||||
}
|
57
DesignKit/Sources/Fonts/UIFont.swift
Normal file
57
DesignKit/Sources/Fonts/UIFont.swift
Normal file
@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
public extension UIFont {
|
||||
|
||||
// MARK: - Convenient methods
|
||||
|
||||
/// Update current font with a SymbolicTraits
|
||||
func withTraits(_ traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
|
||||
guard let descriptor = fontDescriptor.withSymbolicTraits(traits) else {
|
||||
return self
|
||||
}
|
||||
|
||||
// Size 0 means keep the size as it is
|
||||
return UIFont(descriptor: descriptor, size: 0)
|
||||
}
|
||||
|
||||
/// Update current font with a given Weight
|
||||
func withWeight(weight: Weight) -> UIFont {
|
||||
// Add the font weight to the descriptor
|
||||
let weightedFontDescriptor = fontDescriptor.addingAttributes([
|
||||
UIFontDescriptor.AttributeName.traits: [
|
||||
UIFontDescriptor.TraitKey.weight: weight
|
||||
]
|
||||
])
|
||||
return UIFont(descriptor: weightedFontDescriptor, size: 0)
|
||||
}
|
||||
|
||||
// MARK: - Shortcuts
|
||||
|
||||
var bold: UIFont {
|
||||
return withTraits(.traitBold)
|
||||
}
|
||||
|
||||
var semiBold: UIFont {
|
||||
return withWeight(weight: .semibold)
|
||||
}
|
||||
|
||||
var italic: UIFont {
|
||||
return withTraits(.traitItalic)
|
||||
}
|
||||
}
|
125
DesignKit/Sources/TextFields/BorderedInputFieldStyle.swift
Normal file
125
DesignKit/Sources/TextFields/BorderedInputFieldStyle.swift
Normal file
@ -0,0 +1,125 @@
|
||||
//
|
||||
// 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
|
||||
import Introspect
|
||||
|
||||
/// A bordered style of text input
|
||||
///
|
||||
/// As defined in:
|
||||
/// https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=2039%3A26415
|
||||
public struct BorderedInputFieldStyle: TextFieldStyle {
|
||||
@Environment(\.isEnabled) var isEnabled
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
public var isEditing: Bool
|
||||
public var isError: Bool
|
||||
|
||||
private var borderColor: Color {
|
||||
if isError {
|
||||
return .element.alert
|
||||
} else if isEditing {
|
||||
return .element.accent
|
||||
} else {
|
||||
return .element.quinaryContent
|
||||
}
|
||||
}
|
||||
|
||||
private var accentColor: Color {
|
||||
if isError {
|
||||
return .element.alert
|
||||
}
|
||||
return .element.accent
|
||||
}
|
||||
|
||||
private var textColor: Color {
|
||||
if colorScheme == .dark {
|
||||
return isEnabled ? .element.primaryContent : .element.tertiaryContent
|
||||
} else {
|
||||
return isEnabled ? .element.primaryContent : .element.quaternaryContent
|
||||
}
|
||||
}
|
||||
|
||||
private var backgroundColor: Color {
|
||||
if !isEnabled && colorScheme == .dark {
|
||||
return .element.quinaryContent
|
||||
}
|
||||
return .element.background
|
||||
}
|
||||
|
||||
private var placeholderColor: Color {
|
||||
return .element.tertiaryContent
|
||||
}
|
||||
|
||||
private var borderWidth: CGFloat {
|
||||
return isEditing || isError ? 2.0 : 1.5
|
||||
}
|
||||
|
||||
public init(isEditing: Bool = false, isError: Bool = false) {
|
||||
self.isEditing = isEditing
|
||||
self.isError = isError
|
||||
}
|
||||
|
||||
public func _body(configuration: TextField<_Label>) -> some View {
|
||||
let rect = RoundedRectangle(cornerRadius: 8.0)
|
||||
return configuration
|
||||
.font(.element.callout)
|
||||
.foregroundColor(textColor)
|
||||
.accentColor(accentColor)
|
||||
.frame(height: 48.0)
|
||||
.padding(.horizontal, 8.0)
|
||||
.background(backgroundColor)
|
||||
.clipShape(rect)
|
||||
.overlay(rect.stroke(borderColor, lineWidth: borderWidth))
|
||||
.introspectTextField { textField in
|
||||
textField.returnKeyType = .done
|
||||
textField.clearButtonMode = .whileEditing
|
||||
textField.attributedPlaceholder = NSAttributedString(string: textField.placeholder ?? "",
|
||||
attributes: [NSAttributedString.Key.foregroundColor: UIColor(placeholderColor)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct BorderedInputFieldStyle_Previews: PreviewProvider {
|
||||
public static var states: some View {
|
||||
VStack {
|
||||
TextField("Placeholder", text: .constant(""))
|
||||
.textFieldStyle(BorderedInputFieldStyle())
|
||||
TextField("Placeholder", text: .constant(""))
|
||||
.textFieldStyle(BorderedInputFieldStyle(isEditing: true))
|
||||
TextField("Placeholder", text: .constant("Web"))
|
||||
.textFieldStyle(BorderedInputFieldStyle())
|
||||
TextField("Placeholder", text: .constant("Web"))
|
||||
.textFieldStyle(BorderedInputFieldStyle(isEditing: true))
|
||||
TextField("Placeholder", text: .constant("Web"))
|
||||
.textFieldStyle(BorderedInputFieldStyle())
|
||||
.disabled(true)
|
||||
TextField("Placeholder", text: .constant("Web"))
|
||||
.textFieldStyle(BorderedInputFieldStyle(isEditing: true, isError: true))
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
public static var previews: some View {
|
||||
Group {
|
||||
states
|
||||
.preferredColorScheme(.light)
|
||||
states
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
}
|
28
DesignKit/Sources/TextFields/RoundedCornerShape.swift
Normal file
28
DesignKit/Sources/TextFields/RoundedCornerShape.swift
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
struct RoundedCornerShape: Shape {
|
||||
|
||||
let radius: CGFloat
|
||||
let corners: UIRectCorner
|
||||
|
||||
func path(in rect: CGRect) -> Path {
|
||||
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
|
||||
return Path(path.cgPath)
|
||||
}
|
||||
}
|
8
DesignKitTests/DesignKitTests.swift
Normal file
8
DesignKitTests/DesignKitTests.swift
Normal file
@ -0,0 +1,8 @@
|
||||
import XCTest
|
||||
@testable import DesignKit
|
||||
|
||||
final class DesignKitTests: XCTestCase {
|
||||
func testExample() throws {
|
||||
XCTAssert(true)
|
||||
}
|
||||
}
|
23
Package.resolved
Normal file
23
Package.resolved
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "element-design-tokens",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vector-im/element-design-tokens.git",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "ed45767113b703dad6e66d33ad7da388066504f8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftui-introspect",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
|
||||
"state" : {
|
||||
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
|
||||
"version" : "0.1.4"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
27
Package.swift
Normal file
27
Package.swift
Normal file
@ -0,0 +1,27 @@
|
||||
// swift-tools-version: 5.6
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "DesignKit",
|
||||
platforms: [.iOS(.v14)],
|
||||
products: [
|
||||
.library(name: "DesignKit", targets: ["DesignKit"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/vector-im/element-design-tokens.git", branch: "main"),
|
||||
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", .upToNextMajor(from: "0.1.4"))
|
||||
],
|
||||
targets: [
|
||||
.target(name: "DesignKit",
|
||||
dependencies: [
|
||||
.product(name: "DesignTokens", package: "element-design-tokens"),
|
||||
.product(name: "Introspect", package: "SwiftUI-Introspect")
|
||||
],
|
||||
path: "DesignKit"),
|
||||
.testTarget(name: "DesignKitTests",
|
||||
dependencies: ["DesignKit"],
|
||||
path: "DesignKitTests")
|
||||
]
|
||||
)
|
1
changelog.d/43.feature
Normal file
1
changelog.d/43.feature
Normal file
@ -0,0 +1 @@
|
||||
DesignKit: Add initial implementation of DesignKit to the repo as a Swift package.
|
Loading…
x
Reference in New Issue
Block a user