Feat: Working on pressure estimations feature
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct InfoButton: View {
|
||||
|
||||
@Environment(\.infoButtonStyle) private var infoButtonStyle
|
||||
|
||||
let action: () -> Void
|
||||
|
||||
public init(action: @escaping () -> Void) {
|
||||
@@ -11,5 +14,22 @@ public struct InfoButton: View {
|
||||
Button(action: action) {
|
||||
Label("Info", systemImage: "info.circle")
|
||||
}
|
||||
.buttonStyle(infoButtonStyle)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ResetButton: View {
|
||||
|
||||
@Environment(\.resetButtonStyle) private var resetButtonStyle
|
||||
|
||||
let action: () -> Void
|
||||
|
||||
public init(action: @escaping () -> Void) {
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Button("Reset") { action() }
|
||||
.buttonStyle(resetButtonStyle)
|
||||
}
|
||||
}
|
||||
|
||||
21
Sources/Styleguide/FanTypePicker.swift
Normal file
21
Sources/Styleguide/FanTypePicker.swift
Normal file
@@ -0,0 +1,21 @@
|
||||
import SharedModels
|
||||
import SwiftUI
|
||||
|
||||
public struct FanTypePicker: View {
|
||||
|
||||
@Binding var selection: FanType
|
||||
|
||||
public init(selection: Binding<FanType>) {
|
||||
self._selection = selection
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Picker("Fan Type", selection: $selection) {
|
||||
ForEach(FanType.allCases) {
|
||||
Text($0.description)
|
||||
.tag($0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
142
Sources/Styleguide/FlaggedView.swift
Normal file
142
Sources/Styleguide/FlaggedView.swift
Normal file
@@ -0,0 +1,142 @@
|
||||
import SharedModels
|
||||
import SwiftUI
|
||||
|
||||
public struct FlaggedView<Label: View>: View {
|
||||
|
||||
@Environment(\.flaggedViewStyle) private var flaggedViewStyle
|
||||
|
||||
let label: () -> Label
|
||||
let flagged: Flagged
|
||||
|
||||
public init(
|
||||
flagged: Flagged,
|
||||
@ViewBuilder label: @escaping () -> Label
|
||||
) {
|
||||
self.label = label
|
||||
self.flagged = flagged
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
flaggedViewStyle.makeBody(
|
||||
configuration: .init(flagged: flagged, label: .init(label()))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedView where Label == Text {
|
||||
public init(_ title: LocalizedStringKey, flagged: Flagged) {
|
||||
self.init(flagged: flagged) { Text(title) }
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedView where Label == EmptyView {
|
||||
public init(flagged: Flagged) {
|
||||
self.init(flagged: flagged) { EmptyView() }
|
||||
}
|
||||
}
|
||||
|
||||
//public struct FlaggedView: View {
|
||||
//
|
||||
// public let style: Style?
|
||||
// public let title: String
|
||||
// public let flagged: Flagged
|
||||
// public let fractionLength: Int
|
||||
//
|
||||
// public init(
|
||||
// style: Style? = nil,
|
||||
// title: String,
|
||||
// flagged: Flagged,
|
||||
// fractionLength: Int = 3
|
||||
// ) {
|
||||
// self.style = style
|
||||
// self.title = title
|
||||
// self.flagged = flagged
|
||||
// self.fractionLength = fractionLength
|
||||
// }
|
||||
//
|
||||
// public init(
|
||||
// _ title: String,
|
||||
// flagged: Flagged,
|
||||
// style: Style? = nil,
|
||||
// fractionLength: Int = 3
|
||||
// ) {
|
||||
// self.style = style
|
||||
// self.title = title
|
||||
// self.flagged = flagged
|
||||
// self.fractionLength = fractionLength
|
||||
// }
|
||||
//
|
||||
// @ViewBuilder
|
||||
// private var messageView: some View {
|
||||
// if let message = flagged.message {
|
||||
// HStack {
|
||||
// Text(flagged.projectedValue.key.title)
|
||||
// .bold()
|
||||
// .foregroundStyle(flagged.projectedValue.key.flagColor)
|
||||
//
|
||||
// Text(message)
|
||||
// .foregroundStyle(Color.secondary)
|
||||
// }
|
||||
// .font(.caption)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public var body: some View {
|
||||
// switch style {
|
||||
// case .none:
|
||||
// VStack(alignment: .leading, spacing: 8) {
|
||||
// HStack {
|
||||
// Text(title)
|
||||
// .foregroundStyle(Color.secondary)
|
||||
// Spacer()
|
||||
// Text(
|
||||
// flagged.wrappedValue,
|
||||
// format: .number.precision(.fractionLength(fractionLength))
|
||||
// )
|
||||
// Image(systemName: "flag.fill")
|
||||
// .foregroundStyle(flagged.projectedValue.key.flagColor)
|
||||
// }
|
||||
// messageView
|
||||
// }
|
||||
// case .some(.gridRow):
|
||||
// GridRow {
|
||||
// VStack(alignment: .leading, spacing: 8) {
|
||||
// Text(title)
|
||||
// .foregroundStyle(Color.secondary)
|
||||
// messageView
|
||||
// }
|
||||
//
|
||||
// Spacer()
|
||||
//
|
||||
// Text(flagged.wrappedValue, format: .number.precision(.fractionLength(fractionLength)))
|
||||
// .gridCellAnchor(.trailing)
|
||||
//
|
||||
// Image(systemName: "flag.fill")
|
||||
// .foregroundStyle(flagged.flagColor)
|
||||
// .gridCellAnchor(.trailing)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public enum Style {
|
||||
// case gridRow
|
||||
// }
|
||||
//
|
||||
// public static func gridRow(_ title: String, flagged: Flagged) -> Self {
|
||||
// .init(style: .gridRow, title: title, flagged: flagged)
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//#Preview {
|
||||
// List {
|
||||
// Grid(horizontalSpacing: 20) {
|
||||
// FlaggedView(style: .gridRow, title: "Test", flagged: .error(message: "Error message"))
|
||||
// FlaggedView(style: .gridRow, title: "Test", flagged: .init(wrappedValue: 0.5, .result(.good("Good message"))))
|
||||
// }
|
||||
//
|
||||
// FlaggedView(style: nil, title: "Test", flagged: .error(message: "Error message"))
|
||||
// FlaggedView(style: nil, title: "Test", flagged: .init(wrappedValue: 0.5, .result(.good("Good message"))))
|
||||
// }
|
||||
//}
|
||||
21
Sources/Styleguide/FocusableField.swift
Normal file
21
Sources/Styleguide/FocusableField.swift
Normal file
@@ -0,0 +1,21 @@
|
||||
import Foundation
|
||||
|
||||
public protocol FocusableField: Hashable {
|
||||
var next: Self? { get }
|
||||
}
|
||||
|
||||
extension FocusableField where Self: CaseIterable, Self: Equatable {
|
||||
|
||||
public var next: Self? {
|
||||
|
||||
guard let index = Self.allCases.firstIndex(of: self)
|
||||
else { return nil }
|
||||
|
||||
let endIndex = Self.allCases.endIndex
|
||||
let nextIndex = Self.allCases.index(after: index)
|
||||
|
||||
guard nextIndex < endIndex else { return nil }
|
||||
return Self.allCases[nextIndex]
|
||||
|
||||
}
|
||||
}
|
||||
90
Sources/Styleguide/Styles/ButtonStyles.swift
Normal file
90
Sources/Styleguide/Styles/ButtonStyles.swift
Normal file
@@ -0,0 +1,90 @@
|
||||
import SwiftUI
|
||||
|
||||
/// A name space for info button styles.
|
||||
public enum InfoButtonType { }
|
||||
|
||||
/// A name space for info button styles.
|
||||
public enum ResetButtonType { }
|
||||
|
||||
|
||||
public struct AnyButtonStyle<ButtonType>: ButtonStyle {
|
||||
private let _makeBody: (Configuration) -> AnyView
|
||||
|
||||
public init<S: ButtonStyle>(_ style: S) {
|
||||
self._makeBody = { configuration in
|
||||
AnyView(style.makeBody(configuration: configuration))
|
||||
}
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
self._makeBody(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct DefaultInfoButtonStyle: ButtonStyle {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.buttonStyle(.plain)
|
||||
.foregroundStyle(Color.accentColor)
|
||||
.labelStyle(.iconOnly)
|
||||
}
|
||||
}
|
||||
|
||||
extension AnyButtonStyle where ButtonType == InfoButtonType {
|
||||
public static var `default`: Self {
|
||||
.init(DefaultInfoButtonStyle())
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultResetButtonStyle: ButtonStyle {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
}
|
||||
|
||||
extension AnyButtonStyle where ButtonType == ResetButtonType {
|
||||
public static var `default`: Self {
|
||||
.init(DefaultResetButtonStyle())
|
||||
}
|
||||
}
|
||||
|
||||
private struct InfoButtonStyleKey: EnvironmentKey {
|
||||
static var defaultValue = AnyButtonStyle<InfoButtonType>.default
|
||||
}
|
||||
|
||||
private struct ResetButtonStyleKey: EnvironmentKey {
|
||||
static var defaultValue = AnyButtonStyle<ResetButtonType>.default
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
public var infoButtonStyle: AnyButtonStyle<InfoButtonType> {
|
||||
get { self[InfoButtonStyleKey.self] }
|
||||
set { self[InfoButtonStyleKey.self] = newValue }
|
||||
}
|
||||
|
||||
public var resetButtonStyle: AnyButtonStyle<ResetButtonType> {
|
||||
get { self[ResetButtonStyleKey.self] }
|
||||
set { self[ResetButtonStyleKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
public func infoButtonStyle(_ style: AnyButtonStyle<InfoButtonType>) -> some View {
|
||||
environment(\.infoButtonStyle, AnyButtonStyle(style))
|
||||
}
|
||||
|
||||
public func infoButtonStyle<S: ButtonStyle>(_ style: S) -> some View {
|
||||
infoButtonStyle(AnyButtonStyle(style))
|
||||
}
|
||||
|
||||
public func resetButtonStyle(_ style: AnyButtonStyle<ResetButtonType>) -> some View {
|
||||
environment(\.resetButtonStyle, AnyButtonStyle(style))
|
||||
}
|
||||
|
||||
public func resetButtonStyle<S: ButtonStyle>(_ style: S) -> some View {
|
||||
resetButtonStyle(AnyButtonStyle(style))
|
||||
}
|
||||
}
|
||||
176
Sources/Styleguide/Styles/FlaggedViewStyle.swift
Normal file
176
Sources/Styleguide/Styles/FlaggedViewStyle.swift
Normal file
@@ -0,0 +1,176 @@
|
||||
import SharedModels
|
||||
import SwiftUI
|
||||
|
||||
public protocol FlaggedViewStyle {
|
||||
associatedtype Body: View
|
||||
typealias Configuration = FlaggedViewStyleConfiguration
|
||||
|
||||
@ViewBuilder
|
||||
func makeBody(configuration: Self.Configuration) -> Self.Body
|
||||
}
|
||||
|
||||
public struct FlaggedViewStyleConfiguration {
|
||||
public let flagged: Flagged
|
||||
public let label: Label
|
||||
|
||||
/// A type erased label for a flagged view.
|
||||
public struct Label: View {
|
||||
public let body: AnyView
|
||||
|
||||
public init<Content: View>(_ content: Content) {
|
||||
self.body = AnyView(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct AnyFlaggedViewStyle: FlaggedViewStyle {
|
||||
private var _makeBody: (Configuration) -> AnyView
|
||||
|
||||
internal init(makeBody: @escaping (Configuration) -> AnyView) {
|
||||
self._makeBody = makeBody
|
||||
}
|
||||
|
||||
public init<S: FlaggedViewStyle>(style: S) {
|
||||
self.init { configuration in
|
||||
AnyView(style.makeBody(configuration: configuration))
|
||||
}
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
_makeBody(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultFlagViewStyle: FlaggedViewStyle {
|
||||
|
||||
let alignment: HorizontalAlignment
|
||||
let fractionLength: Int
|
||||
let spacing: CGFloat
|
||||
|
||||
init(
|
||||
alignment: HorizontalAlignment = .leading,
|
||||
fractionLength: Int = 2,
|
||||
spacing: CGFloat = 8
|
||||
) {
|
||||
self.alignment = alignment
|
||||
self.fractionLength = fractionLength
|
||||
self.spacing = spacing
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
VStack(alignment: alignment, spacing: spacing) {
|
||||
HStack {
|
||||
configuration.label
|
||||
Spacer()
|
||||
Text(
|
||||
configuration.flagged.wrappedValue,
|
||||
format: .number.precision(.fractionLength(fractionLength))
|
||||
)
|
||||
configuration.flagged.flagImage
|
||||
}
|
||||
flaggedMessageView(flagged: configuration.flagged)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct FlagAndMessageOnlyStyle: FlaggedViewStyle {
|
||||
|
||||
public enum StackStyle {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
let stackStyle: StackStyle
|
||||
|
||||
@ViewBuilder
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
switch stackStyle {
|
||||
case .horizontal:
|
||||
HStack {
|
||||
flaggedMessageView(flagged: configuration.flagged)
|
||||
configuration.flagged.flagImage
|
||||
}
|
||||
case .vertical:
|
||||
VStack {
|
||||
configuration.flagged.flagImage
|
||||
flaggedMessageView(flagged: configuration.flagged)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedViewStyle where Self == FlagAndMessageOnlyStyle {
|
||||
public static func flagAndMessageOnly(_ stackStyle: FlagAndMessageOnlyStyle.StackStyle = .vertical) -> Self {
|
||||
.init(stackStyle: stackStyle)
|
||||
}
|
||||
}
|
||||
|
||||
private struct FlaggedViewStyleKey: EnvironmentKey {
|
||||
static let defaultValue = AnyFlaggedViewStyle(style: DefaultFlagViewStyle())
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
public var flaggedViewStyle: AnyFlaggedViewStyle {
|
||||
get { self[FlaggedViewStyleKey.self] }
|
||||
set { self[FlaggedViewStyleKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension FlaggedViewStyle where Self == DefaultFlagViewStyle {
|
||||
public static func `default`(alignment: HorizontalAlignment = .leading, fractionLength: Int = 2, spacing: CGFloat = 8) -> Self {
|
||||
.init(alignment: alignment, fractionLength: fractionLength, spacing: spacing)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Flagged.CheckResult.Key {
|
||||
|
||||
public var flagColor: Color {
|
||||
switch self {
|
||||
case .good:
|
||||
return .green
|
||||
case .warning:
|
||||
return .yellow
|
||||
case .error:
|
||||
return .red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Flagged {
|
||||
public var flagColor: Color { projectedValue.key.flagColor }
|
||||
|
||||
public var flagImage: some View {
|
||||
Image(systemName: "flag.fill")
|
||||
.foregroundStyle(flagColor)
|
||||
}
|
||||
|
||||
public var messageView: some View { flaggedMessageView(flagged: self) }
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func flaggedMessageView(flagged: Flagged) -> some View {
|
||||
if let message = flagged.message {
|
||||
HStack {
|
||||
Text(flagged.projectedValue.key.title)
|
||||
.bold()
|
||||
.foregroundStyle(flagged.projectedValue.key.flagColor)
|
||||
TextLabel(message)
|
||||
}
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func flaggedViewStyle(_ style: AnyFlaggedViewStyle) -> some View {
|
||||
environment(\.flaggedViewStyle, style)
|
||||
}
|
||||
|
||||
public func flaggedViewStyle<S: FlaggedViewStyle>(
|
||||
_ style: S
|
||||
) -> some View {
|
||||
environment(\.flaggedViewStyle, AnyFlaggedViewStyle(style: style))
|
||||
}
|
||||
}
|
||||
175
Sources/Styleguide/Styles/TextLabelStyle.swift
Normal file
175
Sources/Styleguide/Styles/TextLabelStyle.swift
Normal file
@@ -0,0 +1,175 @@
|
||||
import SwiftUI
|
||||
|
||||
public protocol TextLabelStyle {
|
||||
|
||||
associatedtype Body: View
|
||||
typealias Configuration = TextLabelConfiguration
|
||||
|
||||
func makeBody(configuration: Self.Configuration) -> Self.Body
|
||||
}
|
||||
|
||||
extension TextLabelStyle {
|
||||
public func combining<Other: TextLabelStyle>(_ style: Other) -> AnyTextLabelStyle {
|
||||
AnyTextLabelStyle(style: self).combining(style)
|
||||
}
|
||||
}
|
||||
|
||||
public struct TextLabelConfiguration {
|
||||
|
||||
/// A type erased label for a text label.
|
||||
public struct Label: View {
|
||||
public var body: AnyView
|
||||
|
||||
public init<Content: View>(content: Content) {
|
||||
body = AnyView(content)
|
||||
}
|
||||
}
|
||||
|
||||
public let label: TextLabelConfiguration.Label
|
||||
}
|
||||
|
||||
public struct AnyTextLabelStyle: TextLabelStyle {
|
||||
private var _makeBody: (Configuration) -> AnyView
|
||||
|
||||
internal init(makeBody: @escaping (Configuration) -> AnyView) {
|
||||
self._makeBody = makeBody
|
||||
}
|
||||
|
||||
public init<S: TextLabelStyle>(style: S) {
|
||||
self._makeBody = { configuration in
|
||||
AnyView(style.makeBody(configuration: configuration))
|
||||
}
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
_makeBody(configuration)
|
||||
}
|
||||
|
||||
public func combining<S: TextLabelStyle>(_ style: S) -> Self {
|
||||
Self.init { configuration in
|
||||
AnyView(
|
||||
self.makeBody(
|
||||
configuration: TextLabelConfiguration(
|
||||
label: .init(content: style.makeBody(configuration: configuration))
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct AutomaticTextLabelStyle: TextLabelStyle {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
}
|
||||
}
|
||||
|
||||
private struct TextLabelStyleKey: EnvironmentKey {
|
||||
static let defaultValue = AnyTextLabelStyle(style: AutomaticTextLabelStyle())
|
||||
}
|
||||
|
||||
private struct SectionHeaderLabelStyleKey: EnvironmentKey {
|
||||
static let defaultValue = AnyTextLabelStyle(style: AutomaticTextLabelStyle())
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
public var textLabelStyle: AnyTextLabelStyle {
|
||||
get { self[TextLabelStyleKey.self] }
|
||||
set { self[TextLabelStyleKey.self] = newValue }
|
||||
}
|
||||
|
||||
public var sectionHeaderLabelStyle: AnyTextLabelStyle {
|
||||
get { self[SectionHeaderLabelStyleKey.self] }
|
||||
set { self[SectionHeaderLabelStyleKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
public struct FontTextLabelStyle: TextLabelStyle {
|
||||
let font: Font?
|
||||
let fontWeight: Font.Weight?
|
||||
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.font(font)
|
||||
.fontWeight(fontWeight)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ColoredTextLabelStyle: TextLabelStyle {
|
||||
|
||||
let primary: Color
|
||||
let secondary: Color?
|
||||
let tertiary: Color?
|
||||
|
||||
@ViewBuilder
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
switch (secondary, tertiary) {
|
||||
case let (.some(secondary), .some(tertiary)):
|
||||
configuration.label.foregroundStyle(primary, secondary, tertiary)
|
||||
case let (.some(secondary), .none):
|
||||
configuration.label.foregroundStyle(primary, secondary)
|
||||
case let (.none, .some(tertiary)):
|
||||
configuration.label.foregroundStyle(primary, tertiary)
|
||||
case (.none, .none):
|
||||
configuration.label.foregroundStyle(primary)
|
||||
}
|
||||
}
|
||||
|
||||
public func font(_ font: Font? = nil, fontWeight: Font.Weight? = nil) -> AnyTextLabelStyle {
|
||||
self.combining(.font(font, fontWeight: fontWeight))
|
||||
}
|
||||
}
|
||||
|
||||
extension TextLabelStyle where Self == AutomaticTextLabelStyle {
|
||||
public static var automatic: Self {
|
||||
.init()
|
||||
}
|
||||
}
|
||||
|
||||
extension TextLabelStyle where Self == FontTextLabelStyle {
|
||||
public static func font(_ font: Font? = nil, fontWeight: Font.Weight? = nil) -> Self {
|
||||
.init(font: font, fontWeight: fontWeight)
|
||||
}
|
||||
|
||||
public static var heavyTitle2: Self {
|
||||
.font(.title2, fontWeight: .heavy)
|
||||
}
|
||||
}
|
||||
|
||||
extension TextLabelStyle where Self == ColoredTextLabelStyle {
|
||||
|
||||
public static func colored(_ color: Color) -> Self {
|
||||
.init(primary: color, secondary: nil, tertiary: nil)
|
||||
}
|
||||
|
||||
public static func colored(_ primary: Color, _ secondary: Color, _ tertiary: Color? = nil) -> Self {
|
||||
.init(primary: primary, secondary: secondary, tertiary: tertiary)
|
||||
}
|
||||
|
||||
public static var secondary: Self {
|
||||
self.colored(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
extension AnyTextLabelStyle {
|
||||
|
||||
public static var boldSecondary: AnyTextLabelStyle {
|
||||
ColoredTextLabelStyle(primary: .secondary, secondary: nil, tertiary: nil)
|
||||
.combining(.font(fontWeight: .bold))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
public func textLabelStyle(_ style: AnyTextLabelStyle) -> some View {
|
||||
environment(\.textLabelStyle, style)
|
||||
}
|
||||
|
||||
public func textLabelStyle<S: TextLabelStyle>(_ style: S) -> some View {
|
||||
environment(\.textLabelStyle, AnyTextLabelStyle(style: style))
|
||||
}
|
||||
|
||||
public func sectionHeaderLabelStyle<S: TextLabelStyle>(_ style: S) -> some View {
|
||||
environment(\.sectionHeaderLabelStyle, AnyTextLabelStyle(style: style))
|
||||
}
|
||||
}
|
||||
19
Sources/Styleguide/TextField+Precision.swift
Normal file
19
Sources/Styleguide/TextField+Precision.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
import SwiftUI
|
||||
|
||||
extension TextField where Label == Text {
|
||||
|
||||
public init(
|
||||
_ titleKey: LocalizedStringKey,
|
||||
value: Binding<Double?>,
|
||||
fractionLength: Int,
|
||||
prompt: Text? = nil
|
||||
) {
|
||||
self.init(
|
||||
titleKey,
|
||||
value: value,
|
||||
format: .number.precision(.fractionLength(fractionLength)),
|
||||
prompt: prompt
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
52
Sources/Styleguide/TextLabel.swift
Normal file
52
Sources/Styleguide/TextLabel.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
import SwiftUI
|
||||
|
||||
/// A view that can be styled view the `.textLabelStyle` modifier, generally they will be
|
||||
/// simple `Text` views, however it will accept any content view and will apply the style to.
|
||||
///
|
||||
/// Custom styles can be created by conforming to the ``TextLabelStyle`` protocol.
|
||||
///
|
||||
public struct TextLabel<Content: View>: View {
|
||||
|
||||
@Environment(\.textLabelStyle) private var textLabelStyle
|
||||
|
||||
private let content: () -> Content
|
||||
|
||||
public init(@ViewBuilder content: @escaping () -> Content) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
textLabelStyle.makeBody(
|
||||
configuration: TextLabelConfiguration(
|
||||
label: TextLabelConfiguration.Label(content: content())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension TextLabel where Content == Text {
|
||||
public init<S>(_ text: S) where S: StringProtocol {
|
||||
self.init { Text(text) }
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
VStack {
|
||||
TextLabel("Automatic")
|
||||
|
||||
TextLabel("Secondary-Bold")
|
||||
.textLabelStyle(AnyTextLabelStyle.boldSecondary)
|
||||
|
||||
TextLabel("Secondary")
|
||||
.textLabelStyle(.secondary)
|
||||
.padding(.bottom)
|
||||
|
||||
Group {
|
||||
TextLabel("One")
|
||||
TextLabel("Two")
|
||||
TextLabel("Three")
|
||||
}
|
||||
.font(.title)
|
||||
.textLabelStyle(.boldSecondary)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user