281 lines
9.3 KiB
Swift
281 lines
9.3 KiB
Swift
//
|
|
// Typography.swift
|
|
// VDS
|
|
//
|
|
// Created by Matt Bruce on 8/16/22.
|
|
//
|
|
|
|
import Foundation
|
|
import VDSTypographyTokens
|
|
|
|
public enum TextPosition: String, Codable, CaseIterable {
|
|
case left, right, center
|
|
|
|
var textAlignment: NSTextAlignment {
|
|
switch self {
|
|
case .left:
|
|
return NSTextAlignment.left
|
|
case .right:
|
|
return NSTextAlignment.right
|
|
case .center:
|
|
return NSTextAlignment.center
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum TypographicalStyle: String, Codable, CaseIterable {
|
|
|
|
case FeatureXLarge
|
|
case BoldFeatureXLarge
|
|
case FeatureLarge
|
|
case BoldFeatureLarge
|
|
case FeatureMedium
|
|
case BoldFeatureMedium
|
|
case FeatureSmall
|
|
case BoldFeatureSmall
|
|
case FeatureXSmall
|
|
case BoldFeatureXSmall
|
|
|
|
case Title2XLarge
|
|
case BoldTitle2XLarge
|
|
case TitleXLarge
|
|
case BoldTitleXLarge
|
|
case TitleLarge
|
|
case BoldTitleLarge
|
|
case TitleMedium
|
|
case BoldTitleMedium
|
|
case TitleSmall
|
|
case BoldTitleSmall
|
|
|
|
case BodyLarge
|
|
case BoldBodyLarge
|
|
case BodyMedium
|
|
case BoldBodyMedium
|
|
case BodySmall
|
|
case BoldBodySmall
|
|
|
|
case Micro
|
|
case BoldMicro
|
|
|
|
public static var defaultStyle: TypographicalStyle {
|
|
return .BodyLarge
|
|
}
|
|
}
|
|
|
|
//MARK: FontCategory
|
|
extension TypographicalStyle {
|
|
public enum FontCategory: String, Codable, CaseIterable {
|
|
case feature = "Feature"
|
|
case title = "Title"
|
|
case body = "Body"
|
|
case micro = "Micro"
|
|
|
|
public var sizes: [FontSize] {
|
|
switch self {
|
|
case .feature:
|
|
return [.xlarge, .large, .medium, .small, .xsmall]
|
|
case .title:
|
|
return [.xxlarge, .xlarge, .large, .medium, .small]
|
|
case .body:
|
|
return [.large, .medium, .small]
|
|
case .micro:
|
|
return []
|
|
}
|
|
}
|
|
|
|
public func style(for fontSize: FontSize?, isBold: Bool = false) -> TypographicalStyle? {
|
|
let styleName = "\(isBold ? "Bold" : "")\(self.rawValue)\(fontSize?.rawValue ?? "")"
|
|
guard let style = TypographicalStyle(rawValue: styleName) else {
|
|
return nil
|
|
}
|
|
return style
|
|
}
|
|
}
|
|
}
|
|
|
|
//MARK: FontSize
|
|
extension TypographicalStyle {
|
|
public enum FontSize: String, Codable, CaseIterable {
|
|
case xxlarge = "2XLarge"
|
|
case xlarge = "XLarge"
|
|
case large = "Large"
|
|
case medium = "Medium"
|
|
case small = "Small"
|
|
case xsmall = "XSmall"
|
|
}
|
|
}
|
|
|
|
//MARK: PointSize
|
|
extension TypographicalStyle {
|
|
public var pointSize: CGFloat {
|
|
switch self {
|
|
case .FeatureXLarge, .BoldFeatureXLarge:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeFeature144 : VDSTypography.fontSizeFeature96
|
|
case .FeatureLarge, .BoldFeatureLarge:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeFeature128 : VDSTypography.fontSizeFeature80
|
|
case .FeatureMedium, .BoldFeatureMedium:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeFeature96 : VDSTypography.fontSizeFeature64
|
|
case .FeatureSmall, .BoldFeatureSmall:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeFeature80 : VDSTypography.fontSizeFeature48
|
|
case .FeatureXSmall, .BoldFeatureXSmall:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeFeature64 : VDSTypography.fontSizeFeature40
|
|
case .Title2XLarge, .BoldTitle2XLarge:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeTitle64 : VDSTypography.fontSizeTitle40
|
|
case .TitleXLarge, .BoldTitleXLarge:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeTitle48 : VDSTypography.fontSizeTitle32
|
|
case .TitleLarge, .BoldTitleLarge:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeTitle32 : VDSTypography.fontSizeTitle24
|
|
case .TitleMedium, .BoldTitleMedium:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeTitle24 : VDSTypography.fontSizeTitle20
|
|
case .TitleSmall, .BoldTitleSmall:
|
|
return UIDevice.isIPad ? VDSTypography.fontSizeTitle20 : VDSTypography.fontSizeTitle16
|
|
case .BodyLarge, .BoldBodyLarge:
|
|
return VDSTypography.fontSizeBody16
|
|
case .BodyMedium, .BoldBodyMedium:
|
|
return VDSTypography.fontSizeBody14
|
|
case .BodySmall, .BoldBodySmall:
|
|
return VDSTypography.fontSizeBody12
|
|
case .Micro, .BoldMicro:
|
|
return VDSTypography.fontSizeMicro11
|
|
}
|
|
}
|
|
}
|
|
|
|
//MARK: LineHeight
|
|
extension TypographicalStyle {
|
|
public var lineHeight: CGFloat {
|
|
switch self {
|
|
case .FeatureXLarge, .BoldFeatureXLarge:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightFeature136 : VDSTypography.lineHeightFeature88
|
|
case .FeatureLarge, .BoldFeatureLarge:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightFeature120 : VDSTypography.lineHeightFeature76
|
|
case .FeatureMedium, .BoldFeatureMedium:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightFeature88 : VDSTypography.lineHeightFeature64
|
|
case .FeatureSmall, .BoldFeatureSmall:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightFeature76 : VDSTypography.lineHeightFeature48
|
|
case .FeatureXSmall, .BoldFeatureXSmall:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightFeature64 : VDSTypography.lineHeightFeature40
|
|
case .Title2XLarge, .BoldTitle2XLarge:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightTitle64 : VDSTypography.lineHeightTitle40
|
|
case .TitleXLarge, .BoldTitleXLarge:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightTitle48 : VDSTypography.lineHeightTitle36
|
|
case .TitleLarge, .BoldTitleLarge:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightTitle36 : VDSTypography.lineHeightTitle28
|
|
case .TitleMedium, .BoldTitleMedium:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightTitle28 : VDSTypography.lineHeightTitle24
|
|
case .TitleSmall, .BoldTitleSmall:
|
|
return UIDevice.isIPad ? VDSTypography.lineHeightTitle24 : VDSTypography.lineHeightTitle20
|
|
case .BodyLarge, .BoldBodyLarge:
|
|
return VDSTypography.lineHeightBody20
|
|
case .BodyMedium, .BoldBodyMedium:
|
|
return VDSTypography.lineHeightBody18
|
|
case .BodySmall, .BoldBodySmall:
|
|
return VDSTypography.lineHeightBody16
|
|
case .Micro, .BoldMicro:
|
|
return VDSTypography.lineHeightMicro16
|
|
}
|
|
}
|
|
}
|
|
|
|
//MARK: LetterSpacing
|
|
extension TypographicalStyle {
|
|
public var letterSpacing: CGFloat {
|
|
switch self {
|
|
case .FeatureXLarge,
|
|
.FeatureLarge,
|
|
.FeatureMedium,
|
|
.FeatureSmall,
|
|
.FeatureXSmall,
|
|
.Title2XLarge,
|
|
.TitleXLarge,
|
|
.TitleLarge:
|
|
return 0.25
|
|
|
|
case .BoldBodyLarge, .BodyLarge,
|
|
.BoldBodyMedium, .BodyMedium:
|
|
return 0.5
|
|
|
|
default:
|
|
return 0.0
|
|
}
|
|
}
|
|
}
|
|
|
|
//MARK: Alignments
|
|
extension TypographicalStyle {
|
|
public var aligments: [TextPosition] {
|
|
return [.left, .center]
|
|
}
|
|
}
|
|
|
|
//MARK: Fonts
|
|
extension TypographicalStyle {
|
|
public var fontFace: Fonts {
|
|
switch self {
|
|
case .BoldFeatureXLarge,
|
|
.BoldFeatureLarge,
|
|
.BoldFeatureMedium,
|
|
.BoldFeatureSmall,
|
|
.BoldFeatureXSmall,
|
|
.BoldTitle2XLarge,
|
|
.BoldTitleXLarge,
|
|
.BoldTitleLarge,
|
|
.BoldTitleMedium,
|
|
.BoldTitleSmall,
|
|
.BoldBodyLarge,
|
|
.BoldBodyMedium:
|
|
return .dsBold
|
|
|
|
case .FeatureXLarge,
|
|
.FeatureLarge,
|
|
.FeatureMedium,
|
|
.FeatureSmall,
|
|
.FeatureXSmall,
|
|
.Title2XLarge,
|
|
.TitleXLarge:
|
|
return .dsLight
|
|
|
|
case .TitleLarge,
|
|
.TitleMedium,
|
|
.TitleSmall,
|
|
.BodyLarge,
|
|
.BodyMedium:
|
|
return .dsRegular
|
|
|
|
case .BoldBodySmall,
|
|
.BoldMicro:
|
|
return .txBold
|
|
|
|
case .BodySmall,
|
|
.Micro:
|
|
return .txRegular
|
|
}
|
|
}
|
|
|
|
public var font: UIFont {
|
|
return fontFace.font(ofSize: pointSize)
|
|
}
|
|
|
|
public var superScriptFont: UIFont {
|
|
return fontFace.font(ofSize: pointSize / 2)
|
|
}
|
|
}
|
|
|
|
extension TypographicalStyle {
|
|
public static func style(for fontName: String, size: CGFloat) -> TypographicalStyle {
|
|
//filter all styles by fontName
|
|
let styles = allCases.filter{$0.fontFace.fontName == fontName }.sorted { lhs, rhs in lhs.pointSize < rhs.pointSize }
|
|
//if there are no styles then return defaultStyle
|
|
guard styles.count > 0 else { return defaultStyle }
|
|
styles.forEach{print("\($0.rawValue): \($0.pointSize)")}
|
|
//if there is an exact match on a style with this pointSize then return it
|
|
if let style = styles.first(where: {$0.pointSize == size }) {
|
|
return style
|
|
} else if let largerIndex = styles.firstIndex(where: { $0.pointSize > size}) { //find the closet one to pointSize
|
|
return styles[max(largerIndex - 1, 0)]
|
|
} else { //return the last style
|
|
return styles.last!
|
|
}
|
|
}
|
|
}
|