diff --git a/Sources/Orbit/Components/Alert.swift b/Sources/Orbit/Components/Alert.swift index 76b9b4c853c..6b5c449e32c 100644 --- a/Sources/Orbit/Components/Alert.swift +++ b/Sources/Orbit/Components/Alert.swift @@ -1,38 +1,48 @@ import SwiftUI -/// Breaks the main user flow to present information. +/// Orbit component that breaks the main user flow to present information. /// -/// There are times when just simple information isn’t enough and the user needs -/// to take additional steps to resolve the problem or get additional details. +/// An ``Alert`` consists of a title, description, icon, optional custom content and at most two actions. /// -/// In such cases, provide additional actions for your message. -/// Alerts use special status buttons to match the button color with the alert color. +/// ```swift +/// Alert("Alert") { +/// Button("Primary") { /* Tap action */ } +/// Button("Secondary") { /* Tap action */ } +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The title and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. /// -/// Use at most two actions in each Alert: one primary and one subtle. +/// A default ``Status/info`` status can be modified by ``status(_:)`` modifier: /// /// ```swift -/// Alert("Alert", description: "Description") { -/// customContent -/// } buttons: { -/// Button("Primary") { /* */ } -/// Button("Secondary") { /* */ } -/// } -/// .status(.warning) +/// Alert("Alert", description: "Please check your visa") +/// .status(.warning) /// ``` /// -/// The button priority can be overridden by using `buttonPriority()` modifier. +/// The default button priority can be overridden by ``buttonPriority(_:)`` modifier: /// /// ```swift /// Alert("Alert") { -/// customContent +/// content /// } buttons: { -/// Button("Secondary Only") { /* */ } -/// .buttonPriority(.secondary) +/// Button("Secondary Only") { +/// // Tap action +/// } +/// .buttonPriority(.secondary) /// } /// ``` /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/alert/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// For compact variant, use ``AlertInline`` component. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by native `fixedSize()` or ``idealSize()`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/alert/) public struct Alert: View { private let title: String @@ -56,7 +66,7 @@ public struct Alert: View { public extension Alert { - /// Creates Orbit Alert component. + /// Creates Orbit ``Alert`` component. init( _ title: String = "", description: String = "", @@ -73,7 +83,7 @@ public extension Alert { } } - /// Creates Orbit Alert component with no buttons. + /// Creates Orbit ``Alert`` component with no buttons. init( _ title: String = "", description: String = "", @@ -89,7 +99,7 @@ public extension Alert { } } - /// Creates Orbit Alert component with custom content and icon. + /// Creates Orbit ``Alert`` component with custom content and icon. init( _ title: String = "", description: String = "", diff --git a/Sources/Orbit/Components/AlertInline.swift b/Sources/Orbit/Components/AlertInline.swift index 33807c5ac36..e549e020802 100644 --- a/Sources/Orbit/Components/AlertInline.swift +++ b/Sources/Orbit/Components/AlertInline.swift @@ -1,12 +1,38 @@ import SwiftUI -/// Breaks the main user flow to present information. +/// Orbit component that breaks the main user flow to present information. /// -/// There are times when just simple information isn’t enough and the user needs -/// to take additional steps to resolve the problem or get additional details. +/// An ``AlertInline`` (an inline variant of ``Alert``) consists of a title, icon and a single action. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/alert/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// ```swift +/// AlertInline("Alert") { +/// Button("Primary") { +/// // Tap action +/// } +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The title and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// A default ``Status/info`` status can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// AlertInline("Alert") { +/// Button("Primary") { +/// // Tap action +/// } +/// } +/// .status(.warning) +/// ``` +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by native `fixedSize()` or ``idealSize()`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/alert/) public struct AlertInline: View { private let title: String @@ -28,7 +54,7 @@ public struct AlertInline: View { public extension AlertInline { - /// Creates Orbit AlertInline component. + /// Creates Orbit ``AlertInline`` component. init( _ title: String = "", icon: Icon.Symbol? = nil, @@ -40,7 +66,7 @@ public extension AlertInline { } } - /// Creates Orbit AlertInline component with custom content and icon. + /// Creates Orbit ``AlertInline`` component with custom content and icon. init( _ title: String = "", isSubtle: Bool = false, diff --git a/Sources/Orbit/Components/Badge.swift b/Sources/Orbit/Components/Badge.swift index 6f9cd435c6e..1c4ce24ed5e 100644 --- a/Sources/Orbit/Components/Badge.swift +++ b/Sources/Orbit/Components/Badge.swift @@ -1,11 +1,37 @@ import SwiftUI -/// Presents users with short, relevant information. +/// Orbit component that displays non-actionable, short and static information. /// -/// Badges are indicators of static information. -/// They can be updated when a status changes, but they should not be actionable. +/// A ``Badge`` consists of a title and up to two icons. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/badge/) +/// ```swift +/// Badge("Label", icon: .grid, type: .lightInverted) +/// ``` +/// +/// ### Customizing appearance +/// +/// The label and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// ```swift +/// Badge("Label", type: .status(nil)) +/// .textColor(.inkNormal) +/// .iconColor(.redNormal) +/// .iconSize(.small) +/// ``` +/// +/// When type is set to ``BadgeType/status(_:inverted:)`` with a `nil` value, the default ``Status/info`` status can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// Badge("Label", type: .status(nil)) +/// .status(.warning) +/// ``` +/// +/// ### Layout +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/badge/) public struct Badge: View, PotentiallyEmptyView { @Environment(\.backgroundShape) private var backgroundShape @@ -47,7 +73,7 @@ public struct Badge: View, PotentiallyEmp } } - @ViewBuilder var resolvedBackground: some View { + @ViewBuilder private var resolvedBackground: some View { if let backgroundShape { backgroundShape.inactiveView } else { @@ -55,11 +81,15 @@ public struct Badge: View, PotentiallyEmp } } - var resolvedLabelColor: Color { + var isEmpty: Bool { + leadingIcon.isEmpty && label.isEmpty && trailingIcon.isEmpty + } + + private var resolvedLabelColor: Color { textColor ?? labelColor } - var labelColor: Color { + private var labelColor: Color { switch type { case .light: return .inkDark case .lightInverted: return .whiteNormal @@ -69,7 +99,7 @@ public struct Badge: View, PotentiallyEmp } } - var defaultBackgroundColor: Color { + private var defaultBackgroundColor: Color { switch type { case .light: return .whiteDarker case .lightInverted: return .inkDark @@ -79,15 +109,11 @@ public struct Badge: View, PotentiallyEmp } } - var minTextWidth: CGFloat { + private var minTextWidth: CGFloat { Text.Size.small.lineHeight * sizeCategory.ratio - .xSmall } - var isEmpty: Bool { - leadingIcon.isEmpty && label.isEmpty && trailingIcon.isEmpty - } - - var defaultStatus: Status { + private var defaultStatus: Status { status ?? .info } } @@ -95,10 +121,7 @@ public struct Badge: View, PotentiallyEmp // MARK: - Inits public extension Badge { - /// Creates Orbit Badge component. - /// - /// - Parameters: - /// - type: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. + /// Creates Orbit ``Badge`` component. init( _ label: String = "", icon: Icon.Symbol? = nil, @@ -112,10 +135,7 @@ public extension Badge { } } - /// Creates Orbit Badge component with custom icons. - /// - /// - Parameters: - /// - style: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. + /// Creates Orbit ``Badge`` component with custom icons. init( _ label: String = "", type: BadgeType = .neutral, @@ -130,6 +150,8 @@ public extension Badge { } // MARK: - Types + +/// A type of Orbit ``Badge`` and ``NotificationBadge``. public enum BadgeType { case light @@ -137,6 +159,7 @@ public enum BadgeType { case neutral case status(_ status: Status? = nil, inverted: Bool = false) + /// An Orbit ``BadgeType`` `status` type with no value provided. public static var status: Self { .status(nil) } diff --git a/Sources/Orbit/Components/BadgeList.swift b/Sources/Orbit/Components/BadgeList.swift index a94ca280b15..7fef28b862d 100644 --- a/Sources/Orbit/Components/BadgeList.swift +++ b/Sources/Orbit/Components/BadgeList.swift @@ -1,19 +1,43 @@ import SwiftUI -/// Presents a list of short details with added visual information. +/// Orbit component that displays non-actionable, short details with added visual information. /// -/// The items in the list should all be static information, *not* actionable. +/// A ``BadgeList`` consists of a description and icon. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/information/badgelist/) +/// ```swift +/// BadgeList("You must collect your baggage", icon: .baggage) +/// ``` +/// +/// ### Customizing appearance +/// +/// The label color can be modified by ``textColor(_:)``: +/// +/// ```swift +/// BadgeList("Label") +/// .textColor(.blueNormal) +/// ``` +/// +/// When type is set to ``BadgeListType/status(_:)`` with a `nil` value, the default ``Status/info`` status can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// BadgeList("Does not include guarantee", type: .status(nil)) +/// .status(.warning) +/// ``` +/// +/// ### Layout +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/information/badgelist/) public struct BadgeList: View, PotentiallyEmptyView { @Environment(\.status) private var status @Environment(\.textAccentColor) private var textAccentColor @Environment(\.textColor) private var textColor - let type: BadgeListType - @ViewBuilder let icon: Icon - @ViewBuilder let content: Content + private let type: BadgeListType + @ViewBuilder private let icon: Icon + @ViewBuilder private let content: Content public var body: some View { if isEmpty == false { @@ -32,7 +56,7 @@ public struct BadgeList: View, PotentiallyEmptyView { } } - @ViewBuilder var badge: some View { + @ViewBuilder private var badge: some View { if icon.isEmpty { Orbit.Icon(.grid) .iconSize(.small) @@ -42,14 +66,18 @@ public struct BadgeList: View, PotentiallyEmptyView { } } - @ViewBuilder var badgeBackground: some View { + @ViewBuilder private var badgeBackground: some View { if icon.isEmpty == false { backgroundColor .clipShape(Circle()) } } + + var isEmpty: Bool { + content.isEmpty && icon.isEmpty + } - public var iconColor: Color { + private var iconColor: Color { switch type { case .neutral: return .inkNormal case .status(let status): return (status ?? defaultStatus).color @@ -57,7 +85,7 @@ public struct BadgeList: View, PotentiallyEmptyView { } } - public var backgroundColor: Color { + private var backgroundColor: Color { switch type { case .neutral: return .cloudLight case .status(let status): return (status ?? defaultStatus).lightColor @@ -65,22 +93,15 @@ public struct BadgeList: View, PotentiallyEmptyView { } } - var defaultStatus: Status { + private var defaultStatus: Status { status ?? .info } - - var isEmpty: Bool { - content.isEmpty && icon.isEmpty - } } // MARK: - Inits public extension BadgeList { - /// Creates Orbit BadgeList component. - /// - /// - Parameters: - /// - type: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. + /// Creates Orbit ``BadgeList`` component. init( _ label: String = "", icon: Icon.Symbol? = nil, @@ -92,10 +113,7 @@ public extension BadgeList { } } - /// Creates Orbit BadgeList component with custom icon. - /// - /// - Parameters: - /// - type: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. + /// Creates Orbit ``BadgeList`` component with custom icon. init( _ label: String = "", type: BadgeListType = .neutral, @@ -108,10 +126,7 @@ public extension BadgeList { } } - /// Creates Orbit BadgeList component with custom icon and content. - /// - /// - Parameters: - /// - type: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. + /// Creates Orbit ``BadgeList`` component with custom icon and content. init( type: BadgeListType = .neutral, @ViewBuilder content: () -> Content, @@ -125,10 +140,17 @@ public extension BadgeList { // MARK: - Types +/// A type of Orbit ``BadgeList``. public enum BadgeListType: Equatable, Hashable { case neutral case status(_ status: Status?) + // FIXME: Remove and use override modifiers case custom(iconColor: Color, backgroundColor: Color) + + /// An Orbit ``BadgeListType`` `status` type with no value provided. + public static var status: Self { + .status(nil) + } } // MARK: - Previews diff --git a/Sources/Orbit/Components/Button.swift b/Sources/Orbit/Components/Button.swift index 0ad340a9a7c..41ace7f57ef 100644 --- a/Sources/Orbit/Components/Button.swift +++ b/Sources/Orbit/Components/Button.swift @@ -1,9 +1,50 @@ import SwiftUI -/// Displays a single important action a user can take. +/// Orbit component that displays a primary control that initiates an action. +/// A counterpart of the native `SwiftUI.Button` with `borderedProminent` button style. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/button/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// A ``Button`` consists of a label and up to two icons. +/// +/// ```swift +/// Button("Continue", icon: .chevronForward) { +/// // Tap action +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The label and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// ```swift +/// Button("Continue") { +/// // Tap action +/// } +/// .textColor(.blueLight) +/// .iconColor(.blueNormal) +/// .iconSize(.large) +/// ``` +/// +/// When type is set to ``ButtonType/status(_:isSubtle:)`` with a `nil` value, +/// the default ``Status/info`` status can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// Button("Delete", type: .status(nil)) { +/// // Tap action +/// } +/// .status(.critical) +/// ``` +/// +/// Before the action is triggered, a relevant haptic feedback, based on the `Button` type, is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. +/// The default ``ButtonSize/regular`` size can be modified by a ``buttonSize(_:)`` modifier. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/button/) public struct Button: View, PotentiallyEmptyView { @Environment(\.suppressButtonStyle) private var suppressButtonStyle @@ -29,7 +70,7 @@ public struct Button: View, PotentiallyEm } } - @ViewBuilder var button: some View { + @ViewBuilder private var button: some View { if isEmpty == false { SwiftUI.Button() { action() @@ -47,12 +88,7 @@ public struct Button: View, PotentiallyEm // MARK: - Inits public extension Button { - /// Creates Orbit Button component. - /// - /// Button size can be specified using `.buttonSize()` modifier. - /// - /// - Parameters: - /// - type: A visual style of component. A style can be optionally modified using `status()` modifier when `nil` status value is provided. + /// Creates Orbit ``Button`` component. init( _ label: String = "", icon: Icon.Symbol? = nil, @@ -69,12 +105,7 @@ public extension Button { } } - /// Creates Orbit Button component with custom icons. - /// - /// Button size can be specified using `.buttonSize()` modifier. - /// - /// - Parameters: - /// - type: A visual style of component. A style can be optionally modified using `status()` modifier when `nil` status value is provided. + /// Creates Orbit ``Button`` component with custom icons. init( _ label: String = "", type: ButtonType = .primary, @@ -92,6 +123,7 @@ public extension Button { // MARK: - Types +/// A predefined type of Orbit ``Button``. public enum ButtonType { case primary case primarySubtle diff --git a/Sources/Orbit/Components/ButtonLink.swift b/Sources/Orbit/Components/ButtonLink.swift index 9e50f69d5de..b5e53329cce 100644 --- a/Sources/Orbit/Components/ButtonLink.swift +++ b/Sources/Orbit/Components/ButtonLink.swift @@ -1,11 +1,51 @@ import SwiftUI -/// Displays a single, less important action a user can take. +/// Orbit component that displays a less important control that initiates an action. +/// A counterpart of the native `SwiftUI.Button` with `plain` button style. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/buttonlink/) +/// A ``ButtonLink`` consists of a label and up to two icons. +/// +/// ```swift +/// ButtonLink("Edit", icon: .edit) { +/// // Tap action +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The icon color can be modified by ``iconColor(_:)`` modifier. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// ```swift +/// ButtonLink("More", icon: .informationCircle) { +/// // Tap action +/// } +/// .iconColor(.blueNormal) +/// .iconSize(.large) +/// ``` +/// +/// When type is set to ``ButtonLinkType/status(_:)`` with a `nil` value, the default ``Status/info`` status can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// ButtonLink("Delete", type: .status(nil)) { +/// // Tap action +/// } +/// .status(.critical) +/// ``` +/// +/// Before the action is triggered, a relevant haptic feedback, based on the `ButtonLink` type, is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier or by specifying the ``ButtonSize/compact`` size. +/// The default ``ButtonSize/regular`` size can be modified by a ``buttonSize(_:)`` modifier. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/buttonlink/) public struct ButtonLink: View, PotentiallyEmptyView { - @Environment(\.suppressButtonStyle) var suppressButtonStyle + @Environment(\.suppressButtonStyle) private var suppressButtonStyle private let label: String private let type: ButtonLinkType @@ -30,7 +70,7 @@ public struct ButtonLink: View, Potential } } - @ViewBuilder var button: some View { + @ViewBuilder private var button: some View { SwiftUI.Button() { action() } label: { @@ -46,12 +86,7 @@ public struct ButtonLink: View, Potential // MARK: - Inits public extension ButtonLink { - /// Creates Orbit ButtonLink component. - /// - /// Button size can be specified using `.buttonSize()` modifier. - /// - /// - Parameters: - /// - type: A visual style of component. A style can be optionally modified using `status()` modifier when `nil` status value is provided. + /// Creates Orbit ``ButtonLink`` component. init( _ label: String = "", type: ButtonLinkType = .primary, @@ -68,12 +103,7 @@ public extension ButtonLink { } } - /// Creates Orbit ButtonLink component with custom icons. - /// - /// Button size can be specified using `.buttonSize()` modifier. - /// - /// - Parameters: - /// - type: A visual style of component. A style can be optionally modified using `status()` modifier when `nil` status value is provided. + /// Creates Orbit ``ButtonLink`` component with custom icons. init( _ label: String = "", type: ButtonLinkType = .primary, @@ -90,6 +120,8 @@ public extension ButtonLink { } // MARK: - Types + +/// A predefined type of Orbit ``ButtonLink``. public enum ButtonLinkType: Equatable { case primary case critical @@ -113,7 +145,7 @@ struct ButtonLinkPreviews: PreviewProvider { static var standalone: some View { VStack(spacing: 0) { - ButtonLink("ButtonLink", action: {}) + ButtonLink("ButtonLink", icon: .grid, action: {}) .buttonSize(.regular) ButtonLink("ButtonLink", type: .critical, action: {}) .buttonSize(.regular) diff --git a/Sources/Orbit/Components/Card.swift b/Sources/Orbit/Components/Card.swift index ad924d29fbe..41f7ec4fc04 100644 --- a/Sources/Orbit/Components/Card.swift +++ b/Sources/Orbit/Components/Card.swift @@ -1,25 +1,42 @@ import SwiftUI -/// Separates content into sections. +/// Orbit component that separates content into sections. /// -/// Card is a wrapping component around a custom content. +/// A ``Card`` consists of a title, description, optional action and a custom content. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/card/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// ```swift +/// Card("Card", description: "Description") { +/// content1 +/// content2 +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The title color can be modified by ``textColor(_:)`` modifier. +/// The default background can be overridden by ``backgroundStyle(_:)-9odue`` modifier. +/// +/// ### Layout +/// +/// The component adds a `VStack` over the provided content. Avoid using the component when the content is large and should be embedded in a lazy stack. +/// +/// Component expands horizontally unless prevented by native `fixedSize()` or ``idealSize()`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/card/) public struct Card: View { @Environment(\.backgroundShape) private var backgroundShape @Environment(\.idealSize) private var idealSize - let title: String - let description: String - let action: CardAction - let headerSpacing: CGFloat - let contentLayout: CardContentLayout - let contentAlignment: HorizontalAlignment - let showBorder: Bool - let titleStyle: Heading.Style - @ViewBuilder let content: Content + private let title: String + private let description: String + private let action: CardAction + private let headerSpacing: CGFloat + private let contentLayout: CardContentLayout + private let contentAlignment: HorizontalAlignment + private let showBorder: Bool + private let titleStyle: Heading.Style + @ViewBuilder private let content: Content public var body: some View { VStack(alignment: .leading, spacing: headerSpacing) { @@ -43,7 +60,7 @@ public struct Card: View { .accessibilityElement(children: .contain) } - @ViewBuilder var header: some View { + @ViewBuilder private var header: some View { if isHeaderEmpty == false { HStack(alignment: .top, spacing: 0) { VStack(alignment: .leading, spacing: .xxSmall) { @@ -77,7 +94,7 @@ public struct Card: View { } } - @ViewBuilder var resolvedBackground: some View { + @ViewBuilder private var resolvedBackground: some View { if let backgroundShape { backgroundShape.inactiveView } else { @@ -85,7 +102,7 @@ public struct Card: View { } } - var isHeaderEmpty: Bool { + private var isHeaderEmpty: Bool { if case .none = action, title.isEmpty, description.isEmpty { return true } else { @@ -93,11 +110,11 @@ public struct Card: View { } } - var isContentEmpty: Bool { + private var isContentEmpty: Bool { content is EmptyView } - var contentPadding: CGFloat { + private var contentPadding: CGFloat { switch contentLayout { case .fill: return 0 case .default: return .medium @@ -105,7 +122,7 @@ public struct Card: View { } } - var contentSpacing: CGFloat { + private var contentSpacing: CGFloat { switch contentLayout { case .fill: return 0 case .default(let spacing): return spacing @@ -117,7 +134,7 @@ public struct Card: View { // MARK: - Inits public extension Card { - /// Creates Orbit Card component. + /// Creates Orbit ``Card`` component. init( _ title: String = "", description: String = "", @@ -151,13 +168,13 @@ public extension AccessibilityID { // MARK: - Types -/// Specifies the trailing action of Card component. +/// The trailing action of Orbit ``Card`` header. public enum CardAction { case none case buttonLink(_ label: String, type: ButtonLinkType = .primary, action: () -> Void) } -/// Specifies the padding and spacing behavior of Card content. +/// The layout of Orbit ``Card`` content. public enum CardContentLayout { /// Content fills all available space with no padding or spacing. case fill diff --git a/Sources/Orbit/Components/CarrierLogo.swift b/Sources/Orbit/Components/CarrierLogo.swift index 8dd5f50f0e4..c37b63de1d0 100644 --- a/Sources/Orbit/Components/CarrierLogo.swift +++ b/Sources/Orbit/Components/CarrierLogo.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Displays logos of transport carriers. +/// Orbit component that displays one or more logos of transport carriers. /// /// Carrier logos can include up to four logos at once. With one logo, by default it occupies the entire space. /// With multiple logos, the logos are shrunk to the same size (no matter how many more there are). @@ -9,98 +9,113 @@ import SwiftUI /// they’re in the top left and bottom right. With three, the second logo shifts to the bottom left /// and the third is present in the top right. With four, the logos take up all four corners. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/visuals/carrierlogo/) -public struct CarrierLogo: View { - - struct SingleCarrierImage: View { - - let image: Image - - init(_ image: Image) { - self.image = image - } - - var body: some View { - image - .resizable() - .cornerRadius(BorderRadius.desktop) - } - } +/// ### Layout +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/visuals/carrierlogo/) +public struct CarrierLogo: View, PotentiallyEmptyView { - let images: [Image] - let size: Icon.Size + private let images: [Image] + private let size: Icon.Size public var body: some View { content .frame(width: size.value, height: size.value) } - @ViewBuilder var content: some View { + @ViewBuilder private var content: some View { switch images.count { case 0: EmptyView() - case 1: SingleCarrierImage(images[0]) + case 1: SingleCarrierImage(image: images[0]) case 2: twoImages case 3: threeImages default: fourImages } } - var twoImages: some View { + @ViewBuilder private var twoImages: some View { VStack(spacing: 0) { HStack(spacing: 0) { - SingleCarrierImage(images[0]) + SingleCarrierImage(image: images[0]) Color.clear } HStack(spacing: 0) { Color.clear - SingleCarrierImage(images[1]) + SingleCarrierImage(image: images[1]) } } } - var threeImages: some View { - VStack(spacing: 2) { - HStack(spacing: 2) { - SingleCarrierImage(images[0]) - SingleCarrierImage(images[2]) + @ViewBuilder private var threeImages: some View { + VStack(spacing: .xxxSmall) { + HStack(spacing: .xxxSmall) { + SingleCarrierImage(image: images[0]) + SingleCarrierImage(image: images[2]) } - HStack(spacing: 2) { - SingleCarrierImage(images[1]) + HStack(spacing: .xxxSmall) { + SingleCarrierImage(image: images[1]) Color.clear } } } - var fourImages: some View { - VStack(spacing: 2) { - HStack(spacing: 2) { - SingleCarrierImage(images[0]) - SingleCarrierImage(images[2]) + @ViewBuilder private var fourImages: some View { + VStack(spacing: .xxxSmall) { + HStack(spacing: .xxxSmall) { + SingleCarrierImage(image: images[0]) + SingleCarrierImage(image: images[2]) } - HStack(spacing: 2) { - SingleCarrierImage(images[1]) - SingleCarrierImage(images[3]) + HStack(spacing: .xxxSmall) { + SingleCarrierImage(image: images[1]) + SingleCarrierImage(image: images[3]) } } } + + var isEmpty: Bool { + images.isEmpty + } +} + +// MARK: - Inits +public extension CarrierLogo { - /// Creates an Orbit `CarrierLogo` component with a single logo image. + /// Creates an Orbit ``CarrierLogo`` component with a single logo image. + /// /// - Parameters: /// - image: a logo image. /// - size: the size of the view. The image will occupy the whole view. - public init(image: Image, size: Icon.Size) { + init(image: Image, size: Icon.Size) { self.images = [image] self.size = size } - /// Creates an Orbit `CarrierLogo` component with multiple images. + /// Creates an Orbit ``CarrierLogo`` component with multiple images. + /// /// - Parameter images: logo images to show in the view. - public init(images: [Image]) { + init(images: [Image]) { self.images = images self.size = .large } } +// MARK: - Types +private extension CarrierLogo { + + struct SingleCarrierImage: View { + + let image: Image + + var body: some View { + image + .resizable() + .cornerRadius(BorderRadius.desktop) + } + } +} + +// MARK: - Previews struct CarrierLogoPreviews: PreviewProvider { static let square = Image(systemName: "square.fill") diff --git a/Sources/Orbit/Components/Checkbox.swift b/Sources/Orbit/Components/Checkbox.swift index 1455d5fdcfc..d5bd8ab22e2 100644 --- a/Sources/Orbit/Components/Checkbox.swift +++ b/Sources/Orbit/Components/Checkbox.swift @@ -1,20 +1,51 @@ import SwiftUI -/// Enables users to pick multiple options from a group. +/// Orbit input component that displays a selectable option to pick from multiple selectable options. +/// A counterpart of the native `SwiftUI.Toggle` with `checkbox` style applied. /// -/// Can be also used to display just the checkbox with no label or description. +/// A ``Checkbox`` consists of a title, description and the checkbox indicator. +/// +/// ```swift +/// Checkbox("Free", isChecked: $isFree) +/// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier: +/// +/// ```swift +/// Checkbox(isChecked: $isFree) +/// .disabled() +/// ``` +/// +/// Before the selection is changed, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Customizing appearance /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/checkbox/) +/// The title color can be modified by ``textColor(_:)`` modifier: +/// +/// ```swift +/// Checkbox("Free", isChecked: $isFree) +/// .textColor(.blueNormal) +/// ``` +/// +/// ### Layout +/// +/// When the provided textual content is empty, the component results in a standalone control. +/// +/// ```swift +/// Checkbox(isChecked: $isFree) +/// ``` +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/checkbox/) public struct Checkbox: View { - @Environment(\.isEnabled) var isEnabled - @Environment(\.textColor) var textColor + @Environment(\.isEnabled) private var isEnabled + @Environment(\.textColor) private var textColor @Environment(\.isHapticsEnabled) private var isHapticsEnabled - let title: String - let description: String - let state: State - @Binding var isChecked: Bool + private let title: String + private let description: String + private let state: State + @Binding private var isChecked: Bool public var body: some View { SwiftUI.Button { @@ -43,13 +74,13 @@ public struct Checkbox: View { ) } - var labelColor: Color { + private var labelColor: Color { isEnabled ? textColor ?? .inkDark : .cloudDarkHover } - var descriptionColor: Color { + private var descriptionColor: Color { isEnabled ? .inkNormal : .cloudDarkHover @@ -59,7 +90,7 @@ public struct Checkbox: View { // MARK: - Inits public extension Checkbox { - /// Creates Orbit Checkbox component. + /// Creates Orbit ``Checkbox`` component. init( _ title: String = "", description: String = "", @@ -73,6 +104,7 @@ public extension Checkbox { // MARK: - Types public extension Checkbox { + /// A state of Orbit ``Checkbox``. enum State { case normal case error diff --git a/Sources/Orbit/Components/ChoiceTile.swift b/Sources/Orbit/Components/ChoiceTile.swift index ab7bbd79cdd..9d2cc459f45 100644 --- a/Sources/Orbit/Components/ChoiceTile.swift +++ b/Sources/Orbit/Components/ChoiceTile.swift @@ -1,9 +1,50 @@ import SwiftUI -/// Enables users to encapsulate radio or checkbox to pick exactly one option from a group. +/// Orbit input component that displays a rich selectable option to pick from a single or multiple selection group. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/choice-tile/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// A ``ChoiceTile`` consists of a title, description, icon, content and a selection indicator. +/// +/// ```swift +/// ChoiceTile("Full", isSelected: $isFull) { +/// // Tap action +/// } content: { +/// // Content +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The title and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// The default background can be overridden by ``backgroundStyle(_:)-9odue`` modifier. +/// +/// ```swift +/// ChoiceTile("Full", icon: .informationCircle, isSelected: $isFull) { +/// // Tap action +/// } +/// .textColor(.blueDark) +/// .iconColor(.inkNormal) +/// .iconSize(.small) +/// .backgroundStyle(.cloudLight) +/// ``` +/// +/// A ``Status`` can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// ChoiceTile("Not available") { +/// // Tap action +/// } +/// .status(.critical) +/// ``` +/// +/// Before the action is triggered, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/choice-tile/) public struct ChoiceTile: View { @Environment(\.backgroundShape) private var backgroundShape @@ -11,22 +52,20 @@ public struct ChoiceTile Void - @ViewBuilder let content: Content - @ViewBuilder let icon: Icon - @ViewBuilder let header: Header - @ViewBuilder let illustration: Illustration + private let title: String + private let description: String + private let badgeOverlay: String + private let indicator: ChoiceTileIndicator + private let titleStyle: Heading.Style + private let isSelected: Bool + private let isError: Bool + private let message: Message? + private let alignment: ChoiceTileAlignment + private let action: () -> Void + @ViewBuilder private let content: Content + @ViewBuilder private let icon: Icon + @ViewBuilder private let header: Header + @ViewBuilder private let illustration: Illustration public var body: some View { SwiftUI.Button { @@ -37,7 +76,7 @@ public struct ChoiceTile: View { - let headerVerticalPadding: CGFloat - let showSeparator: Bool - let isExpanded: Binding? - @ViewBuilder let content: Content - @ViewBuilder let header: Header + private let headerVerticalPadding: CGFloat + private let showSeparator: Bool + private let isExpanded: Binding? + @ViewBuilder private let content: Content + @ViewBuilder private let header: Header public var body: some View { BindingSource(isExpanded, fallbackInitialValue: false) { $isExpanded in @@ -41,16 +49,18 @@ public struct Collapse: View { } } - @ViewBuilder var separator: some View { + @ViewBuilder private var separator: some View { if showSeparator { Separator() } } } +// MARK: - Inits + public extension Collapse { - /// Creates Orbit Collapse component with a binding to the expansion state. + /// Creates Orbit ``Collapse`` component with a binding to the expansion state. init( isExpanded: Binding, showSeparator: Bool = true, @@ -64,7 +74,7 @@ public extension Collapse { self.isExpanded = isExpanded } - /// Creates Orbit Collapse component. + /// Creates Orbit ``Collapse`` component. init(@ViewBuilder content: () -> Content, showSeparator: Bool = true, @ViewBuilder header: () -> Header) { self.header = header() self.headerVerticalPadding = 0 @@ -76,7 +86,7 @@ public extension Collapse { public extension Collapse where Header == Text { - /// Creates Orbit Collapse component with a binding to the expansion state. + /// Creates Orbit ``Collapse`` component with a binding to the expansion state. init(_ title: String, isExpanded: Binding, showSeparator: Bool = true, @ViewBuilder content: () -> Content) { self.header = Text(title) self.headerVerticalPadding = .small @@ -85,7 +95,7 @@ public extension Collapse where Header == Text { self.isExpanded = isExpanded } - /// Creates Orbit Collapse component. + /// Creates Orbit ``Collapse`` component. init(_ title: String, showSeparator: Bool = true, @ViewBuilder content: () -> Content) { self.header = Text(title) self.headerVerticalPadding = .small diff --git a/Sources/Orbit/Components/CountryFlag.swift b/Sources/Orbit/Components/CountryFlag.swift index b945581eff4..c42217f48fd 100644 --- a/Sources/Orbit/Components/CountryFlag.swift +++ b/Sources/Orbit/Components/CountryFlag.swift @@ -1,23 +1,27 @@ import SwiftUI -/// Displays flags of countries from around the world. -/// -/// The component is sized in a similar way as `Icon`, using `iconSize()` modifier. +/// Orbit component that displays flag of a country. /// /// ```swift /// CountryFlag(.us) /// .iconSize(.large) /// ``` /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/countryflag/) +/// ### Layout +/// +/// The component size can be modified by ``iconSize(_:)`` modifier. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/countryflag/) public struct CountryFlag: View, PotentiallyEmptyView { - @Environment(\.textSize) var textSize - @Environment(\.iconSize) var iconSize - @Environment(\.sizeCategory) var sizeCategory + @Environment(\.textSize) private var textSize + @Environment(\.iconSize) private var iconSize + @Environment(\.sizeCategory) private var sizeCategory - let countryCode: CountryCode? - let border: Border + private let countryCode: CountryCode? + private let border: Border public var body: some View { if let countryCode { @@ -35,12 +39,16 @@ public struct CountryFlag: View, PotentiallyEmptyView { .accessibility(label: SwiftUI.Text(countryCode.rawValue)) } } + + var isEmpty: Bool { + countryCode == nil + } - var clipShape: some InsettableShape { + private var clipShape: some InsettableShape { RoundedRectangle(cornerRadius: cornerRadius) } - var cornerRadius: CGFloat { + private var cornerRadius: CGFloat { switch border { case .none: return 0 case .default(let cornerRadius?): return cornerRadius @@ -48,19 +56,15 @@ public struct CountryFlag: View, PotentiallyEmptyView { } } - var size: CGFloat { + private var size: CGFloat { (iconSize ?? textSize.map(Icon.Size.fromTextSize(size:)) ?? Icon.Size.normal.value) * sizeCategory.ratio } - - var isEmpty: Bool { - countryCode == nil - } } // MARK: - Inits public extension CountryFlag { - /// Creates Orbit CountryFlag component. + /// Creates Orbit ``CountryFlag`` component. init(_ countryCode: CountryCode, border: Border = .default()) { self.init( countryCode: countryCode, @@ -68,9 +72,9 @@ public extension CountryFlag { ) } - /// Creates Orbit CountryFlag component with a string country code. + /// Creates Orbit ``CountryFlag`` component using a country code string. /// - /// If a corresponding image is not found, the flag for unknown codes is used. + /// - Note: If a corresponding image is not found, the flag for unknown flag is used. init(_ countryCode: String, border: Border = .default()) { self.init( countryCode: countryCode.isEmpty ? nil : .init(countryCode), @@ -82,6 +86,7 @@ public extension CountryFlag { // MARK: - Types public extension CountryFlag { + /// Orbit ``CountryFlag`` border. enum Border { case none case `default`(cornerRadius: CGFloat? = nil) @@ -94,6 +99,7 @@ public extension CountryFlag { } } + /// Orbit ``CountryFlag`` size. enum Size { case width(_ width: CGFloat) case icon(_ size: Icon.Size = .normal) diff --git a/Sources/Orbit/Components/Coupon.swift b/Sources/Orbit/Components/Coupon.swift index 47de1ac97c2..09c96b3ea94 100644 --- a/Sources/Orbit/Components/Coupon.swift +++ b/Sources/Orbit/Components/Coupon.swift @@ -1,9 +1,19 @@ import SwiftUI -/// Orbit component that highlights promo codes. +/// Orbit component that displays promo codes. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/visuals/coupon/) -public struct Coupon: View { +/// ```swift +/// Coupon("HXT3B81F") +/// .textColor(.blueDark) +/// .textSize(.small) +/// ``` +/// +/// ### Layout +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/visuals/coupon/) +public struct Coupon: View, PotentiallyEmptyView { @Environment(\.textSize) private var textSize @@ -22,12 +32,16 @@ public struct Coupon: View { .foregroundColor(.cloudDark) ) } + + var isEmpty: Bool { + label.isEmpty + } } // MARK: - Inits public extension Coupon { - /// Creates Orbit Coupon component. + /// Creates Orbit ``Coupon`` component. init(_ label: String = "") { self.label = label } diff --git a/Sources/Orbit/Components/Dialog.swift b/Sources/Orbit/Components/Dialog.swift index 9812e4e94fb..e825524c1b8 100644 --- a/Sources/Orbit/Components/Dialog.swift +++ b/Sources/Orbit/Components/Dialog.swift @@ -1,20 +1,50 @@ import SwiftUI -/// Prompts users to take or complete an action. +/// Orbit component that prompts user to complete an action. +/// A counterpart of the native `alert()` modifier. /// -/// Use at most three actions in a Dialog. A Dialog expands both horizontally -/// and vertically and is meant to be used as a modal fullscreen overlay. +/// A ``Dialog`` consists of a title, description, illustration, at most three buttons and an optional custom content. /// /// ```swift /// Dialog("Title") { -/// Button("Primary") { /* */ } -/// Button("Secondary") { /* */ } -/// Button("Tertiary") { /* */ } +/// // Content +/// } buttons: { +/// Button("Primary") { /* Tap action */ } +/// Button("Secondary") { /* Tap action */ } +/// Button("Tertiary") { /* Tap action */ } +/// } illustration: { +/// Illustration(.accommodation) +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// A ``Status`` of buttons can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// Dialog("Title") { +/// Button("Primary") { /* Tap action */ } +/// Button("Secondary") { /* Tap action */ } /// } /// .status(.critical) /// ``` /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/overlay/dialog/) +/// The default button priority can be overridden by ``buttonPriority(_:)`` modifier: +/// +/// ```swift +/// Dialog("Title") { +/// Button("Secondary Only") { +/// // Tap action +/// } +/// .buttonPriority(.secondary) +/// } +/// ``` +/// +/// ### Layout +/// +/// The component expands in both axis and is meant to be used as a fullscreen modal overlay. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/overlay/dialog/) public struct Dialog: View { private let title: String @@ -51,7 +81,7 @@ public struct Dialog: View { .accessibilityElement(children: .contain) } - @ViewBuilder var texts: some View { + @ViewBuilder private var texts: some View { if title.isEmpty == false || description.isEmpty == false { VStack(alignment: .leading, spacing: .xSmall) { Heading(title, style: .title4) @@ -64,7 +94,7 @@ public struct Dialog: View { } } - var shape: some InsettableShape { + private var shape: some InsettableShape { RoundedRectangle(cornerRadius: .small) } } @@ -72,7 +102,7 @@ public struct Dialog: View { // MARK: - Inits extension Dialog { - /// Creates Orbit Dialog component. + /// Creates Orbit ``Dialog`` component. public init( _ title: String = "", description: String = "", diff --git a/Sources/Orbit/Components/EmptyState.swift b/Sources/Orbit/Components/EmptyState.swift index 693e75fdec0..5b578ada04a 100644 --- a/Sources/Orbit/Components/EmptyState.swift +++ b/Sources/Orbit/Components/EmptyState.swift @@ -1,15 +1,41 @@ import SwiftUI -/// Indicates when there’s no data to show, like when a search has no results. +/// Orbit component that indicates there is no data to display. +/// A counterpart of the native `SwiftUI.ContentUnavailableView`. +/// +/// An ``EmptyState`` consists of a title, description, icon, optional custom content and at most two actions. /// /// ```swift -/// EmptyState("No items") { -/// Button("Add item") { /* */ } +/// EmptyState("No travelers") { +/// Button("Add new traveler") { /* Tap action */ } /// } -/// .status(.critical) /// ``` +/// +/// ### Customizing appearance +/// +/// The title color can be modified by ``textColor(_:)`` modifier. +/// +/// A ``Status`` of buttons can be modified by ``status(_:)`` modifier: /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/progress-indicators/emptystate/) +/// ```swift +/// EmptyState("No data") { +/// Button("Add item") { /* Tap action */ } +/// } +/// .status(.warning) +/// ``` +/// +/// The default button priority can be overridden by ``buttonPriority(_:)`` modifier: +/// +/// ```swift +/// EmptyState("No data") { +/// Button("Secondary Only") { +/// // Tap action +/// } +/// .buttonPriority(.secondary) +/// } +/// ``` +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/progress-indicators/emptystate/) public struct EmptyState: View { private let title: String @@ -61,7 +87,7 @@ public struct EmptyState: View // MARK: - Inits public extension EmptyState { - /// Creates Orbit EmptyState component. + /// Creates Orbit ``EmptyState`` component. init( _ title: String = "", description: String = "", @@ -78,7 +104,7 @@ public extension EmptyState { } } - /// Creates Orbit EmptyState component with no action. + /// Creates Orbit ``EmptyState`` component with no action. init( _ title: String = "", description: String = "", diff --git a/Sources/Orbit/Components/Heading.swift b/Sources/Orbit/Components/Heading.swift index 736cc81f135..2de31d67ef4 100644 --- a/Sources/Orbit/Components/Heading.swift +++ b/Sources/Orbit/Components/Heading.swift @@ -1,8 +1,69 @@ import SwiftUI -/// Shows the content hierarchy and improves the reading experience. Also known as Title. +/// Orbit component that displays title that helps to define content hierarchy. +/// A counterpart of the native `SwiftUI.Text` with one of the title font styles applied. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/heading/) +/// A ``Heading`` is created using one of predefined title styles. +/// +/// ```swift +/// Heading("Passengers", style: .title2) +/// ``` +/// +/// ### Customizing appearance +/// +/// Textual properties can be modified in two ways: +/// - Modifiers applied directly to `Heading` that return the same type, such as ``textColor(_:)-80ix5`` or ``textAccentColor(_:)-k54u``. +/// These can also be used for concatenation with other Orbit textual components like ``Text`` or ``Icon``. +/// See the `InstanceMethods` section below for full list. +/// - Modifiers applied to view hierarchy, such as ``textColor(_:)-828ud`` or ``textIsCopyable(_:)``. +/// See a full list of `View` extensions in documentation. +/// +/// ```swift +/// VStack { +/// // Will result in `inkNormal` color +/// Heading("Override", type: .title3) +/// .textColor(.inkNormal) +/// +/// // Will result in `blueDark` color passed from environment +/// Heading("Modified", type: .title3) +/// +/// // Will result in a default `inkDark` color, ignoring the environment value +/// Heading("Default", type: .title3) +/// .textColor(nil) +/// +/// // Will result in mixed `redNormal` and `blueDark` colors +/// Heading("Concatenated", type: .title3) +/// .textColor(.redNormal) +/// + Heading(" Title", type: .title2) +/// .bold() +/// + Icon(.grid) +/// .iconColor(.redNormal) +/// } +/// .textColor(.blueDark) +/// ``` +/// +/// Formatting and behaviour for ``TextLink``s found in the text can be modified by ``textLinkAction(_:)`` and +/// ``textLinkColor(_:)`` modifiers. +/// +/// ```swift +/// Heading("Information for Passenger", type: .title3) +/// .textLinkColor(.inkNormal) +/// .textLinkAction { +/// // TextLink tap Action +/// } +/// ``` +/// +/// ### Layout +/// +/// Orbit text components use a designated vertical padding that is the same in both single and multiline variant. +/// This makes it easier to align them consistently next to other Orbit components like ``Icon`` not only based on the baseline, +/// but also by `top` or `bottom` edges, provided the component text sizes match according to Orbit rules. +/// +/// Orbit text components have a fixed vertical size. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/heading/) public struct Heading: View, FormattedTextBuildable { // Builder properties @@ -27,7 +88,7 @@ public struct Heading: View, FormattedTextBuildable { .accessibility(addTraits: .isHeader) } - @ViewBuilder var textContent: Text { + @ViewBuilder private var textContent: Text { Text(content) .textColor(color) .textSize(custom: style.size) @@ -47,15 +108,11 @@ public struct Heading: View, FormattedTextBuildable { // MARK: - Inits public extension Heading { - /// Creates Orbit Heading component. - /// - /// Modifiers like `textAccentColor()` or `italic()` can be used to further adjust the formatting. - /// To specify the formatting and behaviour for `TextLink`s found in the text, use `textLinkAction()` and - /// `textLinkColor()` modifiers. + /// Creates Orbit ``Heading`` component. /// /// - Parameters: /// - content: String to display. Supports html formatting tags ``, ``, ``, `` and ``. - /// - style: Heading style. + /// - style: A predefined title style. init( _ content: String, style: Style, @@ -72,7 +129,7 @@ public extension Heading { // MARK: - Types public extension Heading { - /// Orbit heading style. + /// Orbit ``Heading`` style that represents the default size and color of a title. enum Style: Equatable { /// 28 pts. case title1 @@ -87,7 +144,7 @@ public extension Heading { /// 13 pts. case title6 - /// Text font size value. + /// Orbit `Heading` ``Heading/Style`` font size. public var size: CGFloat { switch self { case .title1: return 28 @@ -99,7 +156,7 @@ public extension Heading { } } - /// Designated line height. + /// Orbit `Heading` ``Heading/Style`` designated line height. public var lineHeight: CGFloat { switch self { case .title1: return 32 @@ -111,6 +168,7 @@ public extension Heading { } } + /// Orbit `Heading` ``Heading/Style`` font weight. public var weight: Font.Weight { switch self { case .title1, .title4, .title5, .title6: return .bold diff --git a/Sources/Orbit/Components/HorizontalScroll.swift b/Sources/Orbit/Components/HorizontalScroll.swift index 90be84f31e1..48c5fcc9c35 100644 --- a/Sources/Orbit/Components/HorizontalScroll.swift +++ b/Sources/Orbit/Components/HorizontalScroll.swift @@ -1,18 +1,42 @@ import SwiftUI import Combine -public enum HorizontalScrollItemWidth { - /// Width ratio calculated from the available container width. - case ratio(CGFloat = 0.48, maxWidth: CGFloat? = Layout.readableMaxWidth - 270) - /// Custom fixed width. - case fixed(CGFloat) -} - -/// Groups items onto one accessible row even on small screens. +/// Orbit component that groups items onto one accessible scrollable row. +/// In contrast to the native horizontal `SwiftUI.ScrollView`, this component offers snapping to item leading edges both manually and programmatically. +/// +/// A ``HorizontalScroll`` consists of provided content that will be arranged horizontally with the consistent item width and provided spacing. +/// +/// ```swift +/// HorizontalScroll { +/// Tile("Choice 1") +/// Tile("Choice 2") +/// Tile("Choice 3") +/// } +/// ``` +/// +/// The content can be scrolled programatically when wrapped inside ``HorizontalScrollReader``. +/// +/// ```swift +/// HorizontalScrollReader { scrollProxy in +/// HorizontalScroll { +/// Tile("Choice 1") +/// Tile("Choice 2") +/// Tile("Choice 3") +/// } +/// +/// Button("Scroll to first choice") { +/// scrollProxy.scrollTo(0) +/// } +/// } +/// ``` /// -/// Can be used to present similar items (such as cards with baggage options) as a single row even on small screens. +/// ### Layout /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/layout/horizontalscroll/) +/// The component adds `HStack` over the provided content. +/// +/// Component expands horizontally and has a layout similar to the native `ScrollView` with `horizontal` scrollable axis. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/layout/horizontalscroll/) public struct HorizontalScroll: View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass @@ -37,10 +61,10 @@ public struct HorizontalScroll: View { private let snapAnimationDuration: CGFloat = 0.8 private let programaticSnapAnimationDuration: CGFloat = 0.5 - let spacing: CGFloat - let isSnapping: Bool - let itemWidth: HorizontalScrollItemWidth - @ViewBuilder let content: Content + private let spacing: CGFloat + private let isSnapping: Bool + private let itemWidth: HorizontalScrollItemWidth + @ViewBuilder private let content: Content public var body: some View { scrollViewContent @@ -53,7 +77,7 @@ public struct HorizontalScroll: View { .padding(-clippingPadding) } - @ViewBuilder var scrollViewContent: some View { + @ViewBuilder private var scrollViewContent: some View { GeometryReader { geometry in HStack(alignment: .top, spacing: spacing) { content @@ -102,7 +126,7 @@ public struct HorizontalScroll: View { } } - @ViewBuilder var contentHeightReader: some View { + @ViewBuilder private var contentHeightReader: some View { GeometryReader { geometry in Color.clear.preference( key: ContentSizePreferenceKey.self, @@ -111,17 +135,17 @@ public struct HorizontalScroll: View { } } - @ViewBuilder var availableWidthReader: some View { + @ViewBuilder private var availableWidthReader: some View { SingleAxisGeometryReader(axis: .horizontal) { width in Color.clear.preference(key: ScrollViewWidthPreferenceKey.self, value: width) } } - var isIdle: Bool { + private var isIdle: Bool { (scrollingInProgress || isAnimating) == false } - var screenLayoutHorizontalPadding: CGFloat { + private var screenLayoutHorizontalPadding: CGFloat { guard let screenLayoutPadding = screenLayoutPadding else { return spacing } @@ -129,7 +153,7 @@ public struct HorizontalScroll: View { return screenLayoutPadding.horizontal(horizontalSizeClass: horizontalSizeClass) } - var scrollingGesture: some Gesture { + private var scrollingGesture: some Gesture { DragGesture(minimumDistance: .xxxSmall) .onChanged { gesture in updateOffset(gesture: gesture) @@ -139,22 +163,22 @@ public struct HorizontalScroll: View { } } - var scrollStoppingGesture: some Gesture { + private var scrollStoppingGesture: some Gesture { TapGesture() .onEnded { stopSnappingAnimation() } } - var isContentBiggerThanScrollView: Bool { + private var isContentBiggerThanScrollView: Bool { contentSize.width > scrollViewWidth } - var maxOffset: CGFloat { + private var maxOffset: CGFloat { -contentSize.width + scrollViewWidth } - var resolvedItemWidth: CGFloat { + private var resolvedItemWidth: CGFloat { switch itemWidth { case .ratio(let ratio, let maxWidth): let ratio = max(0.05, ratio) @@ -313,15 +337,13 @@ public struct HorizontalScroll: View { // MARK: - Inits public extension HorizontalScroll { - /// Creates Orbit HorizontalScroll component. - /// - /// Can be scrolled programatically when wrapped inside `HorizontalScrollReader`. + /// Creates Orbit ``HorizontalScroll`` component. /// /// - Parameters: /// - isSnapping: Specifies whether the scrolling should snap items to their leading edge. /// - spacing: Spacing between items. /// - itemWidth: Horizontal sizing of each item. - /// - content: Child items that will be horizontally laid out based on above parameters. + /// - content: Items that will be arranged horizontally based on above parameters. init( isSnapping: Bool = true, spacing: CGFloat = .small, @@ -335,6 +357,16 @@ public extension HorizontalScroll { } } +// MARK: - Types + +/// Width for all items used in Orbit ``HorizontalScroll``. +public enum HorizontalScrollItemWidth { + /// Width ratio calculated from the available container width. + case ratio(CGFloat = 0.48, maxWidth: CGFloat? = Layout.readableMaxWidth - 270) + /// Custom fixed width. + case fixed(CGFloat) +} + // MARK: - Preference keys private struct ScrollViewWidthPreferenceKey: PreferenceKey { diff --git a/Sources/Orbit/Components/Icon.swift b/Sources/Orbit/Components/Icon.swift index dc50970b5c1..28230817cff 100644 --- a/Sources/Orbit/Components/Icon.swift +++ b/Sources/Orbit/Components/Icon.swift @@ -1,30 +1,58 @@ import SwiftUI -/// An Orbit Icon component. +/// Orbit component that displays an icon. +/// A counterpart of the native SF Symbols represented by the native `SwiftUI.Image` created using `systemName` parameter. /// -/// Icon is concatenable with Text and Heading components, allowing the construction of multiline texts that include icons. -/// Apart from Orbit symbol, an Icon can be constructed from SF Symbol with matching Orbit icon size. +/// An ``Icon`` is created using predefined Orbit ``Icon/Symbol``. /// /// ```swift -/// Text("2") + Icon("multiply") + Icon(.baggage) +/// Icon(.accomodation) /// ``` /// -/// Icon can be further styled using either `Icon` modifiers or global modifiers `iconColor`, `textColor`, `textFontWeight`, `baselineOffset`. +/// The ``Icon`` can also be created using SF Symbol `String` to make it possible to mix with Orbit components. /// /// ```swift -/// VStack { -/// Icon(.grid) -/// .iconColor(nil) +/// Icon(systemName: "pencil.circle") +/// ``` +/// +/// ### Customizing appearance /// -/// Icon("circle.fill") -/// .fontWeight(.bold) -/// .baselineOffset(-2) +/// Icon properties can be modified in two ways: +/// - Modifiers applied directly to `Icon` that return the same type, such as ``iconColor(_:)-1h3pr`` or ``iconSize(_:)-4p0n3``. +/// These can also be used for concatenation with other Orbit textual components like ``Heading`` or ``Text``. +/// See the `InstanceMethods` section below for full list. +/// - Modifiers applied to view hierarchy, such as ``iconColor(_:)-e48e`` or ``iconSize(_:)-1pg82``. +/// See a full list of `View` extensions in documentation. +/// +/// ```swift +/// VStack { +/// // Will result in `inkNormal` color +/// Icon(.grid) +/// .iconColor(.inkNormal) +/// +/// // Will result in `blueDark` color passed from environment +/// Icon(.grid) +/// +/// // Will result in a default `inkDark` color, ignoring the environment value +/// Icon(.grid) +/// .iconColor(nil) +/// +/// // Will result in mixed `redNormal` and `blueDark` colors +/// Icon(.accomodation) +/// .iconColor(.redNormal) +/// + Icon(.grid) +/// .baselineOffset(-2) +/// + Text(" concatenated ") +/// .bold() /// } -/// .iconColor(.redNormal) -/// .textFontWeight(.medium) +/// .textColor(.blueDark) /// ``` /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/icon/) +/// ### Layout +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/icon/) public struct Icon: View, TextBuildable, PotentiallyEmptyView { /// Approximate size ratio between SF Symbol and Orbit icon symbol. @@ -40,9 +68,9 @@ public struct Icon: View, TextBuildable, PotentiallyEmptyView { @Environment(\.textFontWeight) private var textFontWeight @Environment(\.textSize) private var textSize @Environment(\.sizeCategory) private var sizeCategory - + private let content: Content? - + // Builder properties var baselineOffset: CGFloat? var fontWeight: Font.Weight? @@ -73,6 +101,10 @@ public struct Icon: View, TextBuildable, PotentiallyEmptyView { .alignmentGuide(.lastTextBaseline) { $0[.lastTextBaseline] + resolvedBaselineOffset } } } + + var isEmpty: Bool { + content?.isEmpty ?? true + } private var textRepresentableEnvironment: TextRepresentableEnvironment { .init( @@ -86,11 +118,7 @@ public struct Icon: View, TextBuildable, PotentiallyEmptyView { sizeCategory: sizeCategory ) } - - public var isEmpty: Bool { - content?.isEmpty ?? true - } - + private func sfSymbolFont(_ textRepresentableEnvironment: TextRepresentableEnvironment) -> Font { .system( size: sfSymbolSize(textRepresentableEnvironment) * textRepresentableEnvironment.sizeCategory.ratio, @@ -133,12 +161,12 @@ public struct Icon: View, TextBuildable, PotentiallyEmptyView { // MARK: - Inits public extension Icon { - /// Creates Orbit Icon component for provided Orbit symbol. + /// Creates Orbit ``Icon`` component. init(_ symbol: Icon.Symbol?) { self.init(symbol.map { Icon.Content.symbol($0) }) } - /// Creates Orbit Icon component for provided SF Symbol that matches the Orbit symbol sizing, reflecting the Dynamic Type settings. + /// Creates Orbit ``Icon`` component for provided SF Symbol that matches the Orbit symbol sizing, reflecting the Dynamic Type settings. init(_ systemName: String) { self.init(.sfSymbol(systemName)) } @@ -147,7 +175,7 @@ public extension Icon { // MARK: - Types public extension Icon { - /// Preferred icon size in both dimensions. Actual size may differ based on icon content. + /// Preferred Orbit ``Icon`` size in both dimensions. The actual size may differ based on icon content. enum Size: Equatable { /// Size 16. case small @@ -158,6 +186,7 @@ public extension Icon { /// Size 28. case xLarge + /// Orbit `Icon` ``Icon/Size`` value. public var value: CGFloat { switch self { case .small: return 16 @@ -167,7 +196,7 @@ public extension Icon { } } - /// Icon size matching text line height. + /// Orbit `Icon` ``Icon/Size`` matching the text size. public static func fromTextSize(size: CGFloat) -> CGFloat { Text.Size.lineHeight(forTextSize: size) } @@ -202,7 +231,7 @@ extension Icon: TextRepresentable { } @available(iOS 14.0, *) - func text(textRepresentableEnvironment: TextRepresentableEnvironment) -> SwiftUI.Text? { + private func text(textRepresentableEnvironment: TextRepresentableEnvironment) -> SwiftUI.Text? { switch content { case .none: return nil diff --git a/Sources/Orbit/Components/InputField.swift b/Sources/Orbit/Components/InputField.swift index 94b621e65a0..77270ffa973 100644 --- a/Sources/Orbit/Components/InputField.swift +++ b/Sources/Orbit/Components/InputField.swift @@ -1,21 +1,22 @@ import SwiftUI import UIKit -/// Orbit component that offers a simple text input. The component is an equivalent of the native `TextField` component. +/// Orbit input component that that displays a single-line text input control. +/// A counterpart of the native `SwiftUI.TextField`. +/// +/// The ``InputField`` consists of a label, message and a binding to a string value: /// -/// The InputField is created with an optional label and a binding to a string value. -/// /// ```swift /// InputField("Label", value: $email, prompt: "E-mail") /// .keyboardType(.emailAddress) /// .returnKeyType(.done) /// .focused($isEmailFocused) /// .inputFieldReturnAction { -/// // Action after the value is submitted +/// // Submit action /// } /// ``` /// -/// For secure version, the `passwordStrength` hint can be provided. +/// For secure version, the `passwordStrength` hint can be provided: /// /// ```swift /// InputField( @@ -27,13 +28,25 @@ import UIKit /// ) /// ``` /// -/// The component uses a custom ``TextField`` component (implemented using `UITextField`) that represents the native `TextField`. +/// In order to instantly switch focus between text fields on submit, +/// return `false` from the ``inputFieldShouldReturnAction(_:)-82nse`` after switching the focus. /// +/// ```swift +/// InputField("Label", value: $email, prompt: "E-mail") +/// .focused($focus, equals: .email) +/// .inputFieldShouldReturnAction { +/// focus = .name +/// return false +/// } +/// ``` +/// +/// The component uses a custom ``TextField`` component (implemented using `UITextField`). +/// /// ### Layout /// /// The component expands horizontally. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/inputfield/) +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/inputfield/) public struct InputField: View, TextFieldBuildable { @Environment(\.inputFieldBeginEditingAction) private var inputFieldBeginEditingAction diff --git a/Sources/Orbit/Components/KeyValue.swift b/Sources/Orbit/Components/KeyValue.swift index 551d98a8e1d..04de6de4d94 100644 --- a/Sources/Orbit/Components/KeyValue.swift +++ b/Sources/Orbit/Components/KeyValue.swift @@ -1,13 +1,25 @@ import SwiftUI -/// A pair of label and value to display read-only information. +/// Orbit component that displays a pair of label and a value. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/keyvalue/) +/// A ``KeyValue`` consists of a label and a value that is copyable by default. +/// +/// ```swift +/// KeyValue("First Name", value: "Pavel") +/// ``` +/// +/// ### Layout +/// +/// The text alignment can be modified by ``multilineTextAlignment(_:)``. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/keyvalue/) public struct KeyValue: View, PotentiallyEmptyView { - let key: String - let value: String - let size: Size + private let key: String + private let value: String + private let size: Size public var body: some View { if isEmpty == false { @@ -32,7 +44,7 @@ public struct KeyValue: View, PotentiallyEmptyView { // MARK: - Inits extension KeyValue { - /// Creates Orbit KeyValue component. + /// Creates Orbit ``KeyValue`` component. public init( _ key: String = "", value: String = "", @@ -47,6 +59,7 @@ extension KeyValue { // MARK: - Types extension KeyValue { + /// Orbit ``KeyValue`` size. public enum Size { case normal case large diff --git a/Sources/Orbit/Components/List.swift b/Sources/Orbit/Components/List.swift index 677d18359e5..5ed7e0cccff 100644 --- a/Sources/Orbit/Components/List.swift +++ b/Sources/Orbit/Components/List.swift @@ -1,21 +1,38 @@ import SwiftUI -/// Shows related items. +/// Orbit component that displays vertically arranged related items. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/list/) +/// A ``List`` typically consists of ``ListItem`` content. +/// +/// ```swift +/// List { +/// ListItem("Planes", icon: .airplane) +/// ListItem("Trains") +/// } +/// ``` +/// +/// ### Layout +/// +/// The component arranges list items in a `VStack` aligned to `leading` edge. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/list/) public struct List: View { - let spacing: CGFloat - @ViewBuilder let content: Content + private let spacing: CGFloat + @ViewBuilder private let content: Content public var body: some View { VStack(alignment: .leading, spacing: spacing) { content } } +} - /// Creates Orbit List component, wrapping ListItem content. - public init(spacing: CGFloat = .xSmall, @ViewBuilder content: () -> Content) { +// MARK: - Inits +public extension List { + + /// Creates Orbit ``List`` component that wraps ``ListItem`` content. + init(spacing: CGFloat = .xSmall, @ViewBuilder _ content: () -> Content) { self.spacing = spacing self.content = content() } diff --git a/Sources/Orbit/Components/ListChoice.swift b/Sources/Orbit/Components/ListChoice.swift index 0e2640889e3..839e25d6357 100644 --- a/Sources/Orbit/Components/ListChoice.swift +++ b/Sources/Orbit/Components/ListChoice.swift @@ -1,25 +1,78 @@ import SwiftUI -/// Shows one of a selectable list of items with similar structures. +/// Orbit input component that displays a selectable row as a choice in a group of similar items. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/listchoice/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// A ``ListChoice`` consists of a title, description, icon, disclosure and content. +/// +/// ```swift +/// ListChoice("Profile", icon: .user) { +/// // Tap action +/// } content: { +/// // Content +/// } +/// ``` +/// +/// A secondary header content can be provided using `String` value: +/// +/// ```swift +/// ListChoice(title, description: description, value: value) { +/// // Tap action +/// } +/// ``` +/// +/// or using a custom content: +/// +/// ```swift +/// ListChoice(title, description: description, icon: .user) { +/// // Tap action +/// } content: { +/// // Custom content +/// } header: { +/// // Header trailing content +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The title and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// The default background can be overridden by ``backgroundStyle(_:)-9odue`` modifier. +/// +/// A ``Status`` can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// ListChoice("Not available") { +/// // Action +/// } +/// .status(.critical) +/// ``` +/// +/// Before the action is triggered, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/listchoice/) public struct ListChoice: View, PotentiallyEmptyView { - public let verticalPadding: CGFloat = .small // = 45 height @ normal size + private let verticalPadding: CGFloat = .small // = 45 height @ normal size @Environment(\.idealSize) private var idealSize @Environment(\.isHapticsEnabled) private var isHapticsEnabled - let title: String - let description: String - let value: String - let disclosure: ListChoiceDisclosure - let showSeparator: Bool - let action: () -> Void - @ViewBuilder let content: Content - @ViewBuilder let icon: Icon - @ViewBuilder let header: Header + private let title: String + private let description: String + private let value: String + private let disclosure: ListChoiceDisclosure + private let showSeparator: Bool + private let action: () -> Void + @ViewBuilder private let content: Content + @ViewBuilder private let icon: Icon + @ViewBuilder private let header: Header public var body: some View { if isEmpty == false { @@ -45,7 +98,7 @@ public struct ListChoice: View, Potenti } } - @ViewBuilder var buttonContent: some View { + @ViewBuilder private var buttonContent: some View { HStack(spacing: 0) { VStack(alignment: .leading, spacing: 0) { headerRow @@ -64,7 +117,7 @@ public struct ListChoice: View, Potenti .frame(maxWidth: idealSize.horizontal == true ? nil : .infinity, alignment: .leading) } - @ViewBuilder var headerRow: some View { + @ViewBuilder private var headerRow: some View { if isHeaderEmpty == false || (header is EmptyView) == false { HStack(spacing: 0) { headerTexts @@ -82,7 +135,7 @@ public struct ListChoice: View, Potenti } } - @ViewBuilder var headerTexts: some View { + @ViewBuilder private var headerTexts: some View { if isHeaderEmpty == false { HStack(alignment: .top, spacing: .xSmall) { icon @@ -107,7 +160,7 @@ public struct ListChoice: View, Potenti } } - @ViewBuilder var disclosureView: some View { + @ViewBuilder private var disclosureView: some View { switch disclosure { case .none: EmptyView() @@ -129,7 +182,7 @@ public struct ListChoice: View, Potenti } } - @ViewBuilder func disclosureButton(type: ListChoiceDisclosure.ButtonType) -> some View { + @ViewBuilder private func disclosureButton(type: ListChoiceDisclosure.ButtonType) -> some View { Button(type: type == .add ? .primarySubtle : .criticalSubtle) { // No action } icon: { @@ -142,7 +195,7 @@ public struct ListChoice: View, Potenti .idealSize() } - @ViewBuilder var separator: some View { + @ViewBuilder private var separator: some View { if showSeparator { Separator() } @@ -152,15 +205,15 @@ public struct ListChoice: View, Potenti isHeaderEmpty && header.isEmpty && content.isEmpty && disclosure == .none } - var isHeaderEmpty: Bool { + private var isHeaderEmpty: Bool { icon.isEmpty && isHeaderTextEmpty } - var isHeaderTextEmpty: Bool { + private var isHeaderTextEmpty: Bool { title.isEmpty && description.isEmpty } - var accessibilityTraitsToAdd: AccessibilityTraits { + private var accessibilityTraitsToAdd: AccessibilityTraits { switch disclosure { case .none, .disclosure, .button(.add), .buttonLink, .checkbox(false, _), .radio(false, _), .icon: return [] @@ -168,9 +221,13 @@ public struct ListChoice: View, Potenti return .isSelected } } +} - /// Creates Orbit ListChoice component with custom content. - public init( +// MARK: - Inits +public extension ListChoice { + + /// Creates Orbit ``ListChoice`` component with custom content. + init( _ title: String = "", description: String = "", value: String = "", @@ -191,12 +248,8 @@ public struct ListChoice: View, Potenti self.icon = icon() self.header = header() } -} - -// MARK: - Inits -public extension ListChoice { - - /// Creates Orbit ListChoice component. + + /// Creates Orbit ``ListChoice`` component. init( _ title: String = "", description: String = "", @@ -226,7 +279,7 @@ public extension ListChoice { public extension ListChoice where Header == Text { - /// Creates Orbit ListChoice component with text header value. + /// Creates Orbit ``ListChoice`` component with a text value as a header. init( _ title: String = "", description: String = "", @@ -258,8 +311,10 @@ public extension ListChoice where Header == Text { // MARK: - Types +/// Disclosure used in Orbit ``ListChoice``. public enum ListChoiceDisclosure: Equatable { + /// Orbit ``ListChoiceDisclosure`` button type. public enum ButtonType { case add case remove diff --git a/Sources/Orbit/Components/NotificationBadge.swift b/Sources/Orbit/Components/NotificationBadge.swift index 8f3fe51b343..732d3e6dbf4 100644 --- a/Sources/Orbit/Components/NotificationBadge.swift +++ b/Sources/Orbit/Components/NotificationBadge.swift @@ -1,8 +1,27 @@ import SwiftUI -/// Shows simple, non-interactive information in a circular badge. +/// Orbit component that displays non-actionable, short and static information in a circular badge. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/information/notificationbadge/) +/// A ``NotificationBadge`` consists of a short label or icon. +/// +/// ```swift +/// NotificationBadge("1", type: .lightInverted) +/// ``` +/// +/// ### Customizing appearance +/// +/// The label and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// When type is set to ``BadgeType/status(_:inverted:)`` with a `nil` value, the default ``Status/info`` status can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// NotificationBadge(.user, type: .status(nil)) +/// .status(.warning) +/// ``` +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/information/notificationbadge/) public struct NotificationBadge: View { @Environment(\.backgroundShape) private var backgroundShape @@ -10,8 +29,8 @@ public struct NotificationBadge: View { @Environment(\.sizeCategory) private var sizeCategory @Environment(\.textColor) private var textColor - private let type: BadgeType @ViewBuilder private let content: Content + private let type: BadgeType public var body: some View { if isEmpty == false { @@ -29,7 +48,7 @@ public struct NotificationBadge: View { } - @ViewBuilder var resolvedBackground: some View { + @ViewBuilder private var resolvedBackground: some View { if let backgroundShape { backgroundShape.inactiveView } else { @@ -37,17 +56,7 @@ public struct NotificationBadge: View { } } - var defaultBackgroundColor: Color { - switch type { - case .light: return .whiteDarker - case .lightInverted: return .inkDark - case .neutral: return .cloudLight - case .status(let status, true): return (status ?? defaultStatus).color - case .status(let status, false): return (status ?? defaultStatus).lightColor - } - } - - @ViewBuilder var background: some View { + @ViewBuilder private var background: some View { switch type { case .light: Color.whiteDarker case .lightInverted: Color.inkDark @@ -56,12 +65,22 @@ public struct NotificationBadge: View { case .status(let status, false): (status ?? defaultStatus).lightColor } } + + private var defaultBackgroundColor: Color { + switch type { + case .light: return .whiteDarker + case .lightInverted: return .inkDark + case .neutral: return .cloudLight + case .status(let status, true): return (status ?? defaultStatus).color + case .status(let status, false): return (status ?? defaultStatus).lightColor + } + } - var resolvedTextColor: Color { + private var resolvedTextColor: Color { textColor ?? labelColor } - var labelColor: Color { + private var labelColor: Color { switch type { case .light: return .inkDark case .lightInverted: return .whiteNormal @@ -71,34 +90,28 @@ public struct NotificationBadge: View { } } - var minWidth: CGFloat { + private var minWidth: CGFloat { Text.Size.small.lineHeight * sizeCategory.ratio } - var defaultStatus: Status { + private var defaultStatus: Status { status ?? .info } +} - /// Creates Orbit NotificationBadge component with custom content. - /// - /// - Parameters: - /// - type: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. - public init( +// MARK: - Inits +public extension NotificationBadge { + + /// Creates Orbit ``NotificationBadge`` component with custom content. + init( type: BadgeType = .status(nil), @ViewBuilder content: () -> Content ) { self.type = type self.content = content() } -} - -// MARK: - Inits -public extension NotificationBadge { - - /// Creates Orbit NotificationBadge component containing text. - /// - /// - Parameters: - /// - style: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. + + /// Creates Orbit ``NotificationBadge`` component containing text. init( _ label: String, type: BadgeType = .status(nil) @@ -110,10 +123,7 @@ public extension NotificationBadge { } } - /// Creates Orbit NotificationBadge component containing icon. - /// - /// - Parameters: - /// - style: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided. + /// Creates Orbit ``NotificationBadge`` component containing icon. init( _ icon: Icon.Symbol, type: BadgeType = .status(nil) @@ -126,7 +136,7 @@ public extension NotificationBadge { } // MARK: - Types -public extension NotificationBadge { +private extension NotificationBadge { enum Content { case icon(Icon.Symbol) diff --git a/Sources/Orbit/Components/Radio.swift b/Sources/Orbit/Components/Radio.swift index 531a5919312..ee2e9076239 100644 --- a/Sources/Orbit/Components/Radio.swift +++ b/Sources/Orbit/Components/Radio.swift @@ -1,20 +1,51 @@ import SwiftUI -/// Enables users to pick exactly one option from a group. +/// Orbit input component that displays a single selectable option to pick from a group. +/// A counterpart of the native `SwiftUI.Picker` with `radioGroup` style applied. /// -/// Can be also used to display just the radio rounded indicator. +/// A ``Radio`` consists of a title, description and the radio indicator. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/radio/) +/// ```swift +/// Radio("Free", isChecked: $isFree) +/// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier: +/// +/// ```swift +/// Radio(isChecked: $isFree) +/// .disabled() +/// ``` +/// +/// Before the selection is changed, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Customizing appearance +/// +/// The title color can be modified by ``textColor(_:)`` modifier: +/// +/// ```swift +/// Radio("Free", isChecked: $isFree) +/// .textColor(.blueNormal) +/// ``` +/// +/// ### Layout +/// +/// When the provided textual content is empty, the component results in a standalone control. +/// +/// ```swift +/// Radio(isChecked: $isFree) +/// ``` +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/radio/) public struct Radio: View { @Environment(\.isEnabled) private var isEnabled @Environment(\.textColor) private var textColor @Environment(\.isHapticsEnabled) private var isHapticsEnabled - let title: String - let description: String - let state: State - @Binding var isChecked: Bool + private let title: String + private let description: String + private let state: State + @Binding private var isChecked: Bool public var body: some View { SwiftUI.Button { @@ -43,13 +74,13 @@ public struct Radio: View { ) } - var labelColor: Color { + private var labelColor: Color { isEnabled ? textColor ?? .inkDark : .cloudDarkHover } - var descriptionColor: Color { + private var descriptionColor: Color { isEnabled ? .inkNormal : .cloudDarkHover @@ -59,7 +90,7 @@ public struct Radio: View { // MARK: - Inits public extension Radio { - /// Creates Orbit Radio component. + /// Creates Orbit ``Radio`` component. init( _ title: String = "", description: String = "", @@ -73,6 +104,7 @@ public extension Radio { // MARK: - Types public extension Radio { + /// A state of Orbit ``Radio``. enum State { case normal case error diff --git a/Sources/Orbit/Components/SegmentedSwitch.swift b/Sources/Orbit/Components/SegmentedSwitch.swift index d3cc4868645..fbd4f8b51fe 100644 --- a/Sources/Orbit/Components/SegmentedSwitch.swift +++ b/Sources/Orbit/Components/SegmentedSwitch.swift @@ -1,12 +1,12 @@ import SwiftUI -/// Showing choices from which only one can be selected. +/// Orbit input component that displays two choices to pick from. +/// A counterpart of the native `SwiftUI.Picker` with `segmented` style applied. /// -/// Specify two, or at most, three views in the content closure, -/// giving each an `.identifier` that matches a value -/// of the selection binding: +/// A ``SegmentedSwitch`` consists of a label and two or three choices. +/// Each choice must be given an Orbit `identifier` that matches a value of the selection binding: /// -/// ``` +/// ```swift /// enum Direction { /// case left /// case right @@ -21,21 +21,25 @@ import SwiftUI /// .identifier(Direction.right) /// } /// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. /// -/// SegmentedSwitch can be in error state only in unselected state. -/// -/// - Note: [Orbit definition](https://orbit.kiwi/components/interaction/segmentedswitch/) +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/interaction/segmentedswitch/) public struct SegmentedSwitch: View { @Environment(\.colorScheme) private var colorScheme @Environment(\.idealSize) private var idealSize @Binding private var selection: Selection? - let label: String - let message: Message? - let content: Content + private let label: String + private let message: Message? + private let content: Content - let borderWidth: CGFloat = BorderWidth.active - let horizontalPadding: CGFloat = .small + private let borderWidth: CGFloat = BorderWidth.active + private let horizontalPadding: CGFloat = .small public var body: some View { FieldWrapper(label, message: message) { @@ -61,7 +65,7 @@ public struct SegmentedSwitch: View { .accessibility(hint: .init(message?.description ?? "")) } - @ViewBuilder func selectedSegmentButton(preferences: [IDPreference]) -> some View { + @ViewBuilder private func selectedSegmentButton(preferences: [IDPreference]) -> some View { if let index = preferences.firstIndex(where: isSelected) { segmentButton(preferences: preferences, index: index) { segmentBackground @@ -71,7 +75,7 @@ public struct SegmentedSwitch: View { } } - @ViewBuilder func unselectedSegmentButtons(_ preferences: [IDPreference]) -> some View { + @ViewBuilder private func unselectedSegmentButtons(_ preferences: [IDPreference]) -> some View { ForEach(unselectedPreferences(preferences), id: \.1.id) { index, preference in segmentButton(preferences: preferences, index: index) { Color.clear @@ -83,7 +87,7 @@ public struct SegmentedSwitch: View { .animation(.easeOut(duration: 0.2), value: selection) } - @ViewBuilder func segmentButton( + @ViewBuilder private func segmentButton( preferences: [IDPreference], index: Int, @ViewBuilder label: @escaping () -> some View @@ -114,7 +118,7 @@ public struct SegmentedSwitch: View { } } - @ViewBuilder func noSelectionBackground(_ preferences: [IDPreference]) -> some View { + @ViewBuilder private func noSelectionBackground(_ preferences: [IDPreference]) -> some View { if selection == nil { GeometryReader { geometry in segmentBackground @@ -139,13 +143,13 @@ public struct SegmentedSwitch: View { } } - @ViewBuilder var segmentBackground: some View { + @ViewBuilder private var segmentBackground: some View { (colorScheme == .light ? Color.whiteDarker : Color.cloudDarkActive) .clipShape(RoundedRectangle(cornerRadius: BorderRadius.default - 1)) .padding(borderWidth) } - @ViewBuilder var separator: some View { + @ViewBuilder private var separator: some View { (message?.status?.color ?? .cloudNormal) .frame(width: borderWidth) .frame(maxHeight: .infinity) @@ -168,7 +172,36 @@ public struct SegmentedSwitch: View { idealSize.horizontal == true } - public init( + private func measurements( + index: Int, + preferences: [IDPreference], + idealSize: Bool, + horizontalPadding: CGFloat, + separatorWidth: CGFloat, + in geometry: GeometryProxy + ) -> (width: CGFloat, minX: CGFloat) { + if idealSize { + let minX = preferences.prefix(upTo: index).reduce(into: 0) { finalMinX, preference in + finalMinX += geometry[preference.bounds].width + separatorWidth + horizontalPadding * 2 + } + let width = geometry[preferences[index].bounds].width + horizontalPadding * 2 + + return (width, minX) + } else { + let width = geometry.size.width / CGFloat(preferences.count) + let minX = (geometry[preferences[index].bounds].minX / width).rounded(.down) * width + + return (width, minX) + } + } + +} + +// MARK: - Inits +public extension SegmentedSwitch { + + /// Creates Orbit ``SegmentedSwitch`` component with optional selection. + init( _ label: String = "", selection: Binding, message: Message? = nil, @@ -180,7 +213,8 @@ public struct SegmentedSwitch: View { self.content = content() } - public init( + /// Creates Orbit ``SegmentedSwitch`` component with non-optional selection. + init( _ label: String = "", selection: Binding, message: Message? = nil, @@ -198,29 +232,6 @@ public struct SegmentedSwitch: View { } } -private func measurements( - index: Int, - preferences: [IDPreference], - idealSize: Bool, - horizontalPadding: CGFloat, - separatorWidth: CGFloat, - in geometry: GeometryProxy -) -> (width: CGFloat, minX: CGFloat) { - if idealSize { - let minX = preferences.prefix(upTo: index).reduce(into: 0) { finalMinX, preference in - finalMinX += geometry[preference.bounds].width + separatorWidth + horizontalPadding * 2 - } - let width = geometry[preferences[index].bounds].width + horizontalPadding * 2 - - return (width, minX) - } else { - let width = geometry.size.width / CGFloat(preferences.count) - let minX = (geometry[preferences[index].bounds].minX / width).rounded(.down) * width - - return (width, minX) - } -} - // MARK: - Previews struct SegmentedSwitchPreviews: PreviewProvider { @@ -330,8 +341,8 @@ struct SegmentedSwitchPreviews: PreviewProvider { label: String = "Gender", message: Message? = nil ) -> some View { - StateWrapper(selection) { value in - SegmentedSwitch(label, selection: value, message: message) { + StateWrapper(selection) { $value in + SegmentedSwitch(label, selection: $value, message: message) { Text(firstOption) .identifier(Gender.male) @@ -349,8 +360,8 @@ struct SegmentedSwitchPreviews: PreviewProvider { label: String = "Gender", message: Message? = nil ) -> some View { - StateWrapper(selection) { value in - SegmentedSwitch(label, selection: value, message: message) { + StateWrapper(selection) { $value in + SegmentedSwitch(label, selection: $value, message: message) { Text(firstOption) .identifier(Gender.male) diff --git a/Sources/Orbit/Components/Select.swift b/Sources/Orbit/Components/Select.swift index d5dbe0a7e3e..c86049c42b8 100644 --- a/Sources/Orbit/Components/Select.swift +++ b/Sources/Orbit/Components/Select.swift @@ -1,12 +1,42 @@ import SwiftUI -/// Also known as dropdown. Offers a simple form control to select from many options. +/// Orbit component that displays a dropdown control to select from many options. +/// A counterpart of the native `SwiftUI.Picker` with `default` style. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/select/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// A ``Select`` consists of a label, value, icon and a default trailing disclosure. +/// +/// ```swift +/// Select("Country", value: "Czechia") { +/// // Tap action +/// } +/// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier. +/// +/// ### Customizing appearance +/// +/// The label and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// ```swift +/// Select("Country", value: "Czechia") { +/// // Tap action +/// } +/// .textColor(.blueLight) +/// .iconColor(.blueNormal) +/// .iconSize(.large) +/// ``` +/// +/// Before the action is triggered, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/select/) public struct Select: View { - public let verticalTextPadding: CGFloat = .small // = 44 @ normal text size + private let verticalTextPadding: CGFloat = .small // = 44 @ normal text size @Environment(\.isEnabled) private var isEnabled @Environment(\.isHapticsEnabled) private var isHapticsEnabled @@ -91,7 +121,7 @@ public struct Select: View { // MARK: - Inits public extension Select { - /// Creates Orbit Select component. + /// Creates Orbit ``Select`` component. init( _ label: String = "", prefix: Icon.Symbol? = nil, @@ -123,7 +153,7 @@ public extension Select { } - /// Creates Orbit Select component with custom prefix or suffix. + /// Creates Orbit ``Select`` component with custom prefix or suffix. init( _ label: String = "", value: String?, diff --git a/Sources/Orbit/Components/Separator.swift b/Sources/Orbit/Components/Separator.swift index aeb8d0b8df2..478a281d4a9 100644 --- a/Sources/Orbit/Components/Separator.swift +++ b/Sources/Orbit/Components/Separator.swift @@ -1,15 +1,34 @@ import SwiftUI -/// Separates content. +/// Orbit component that displays vertical separator between content. +/// +/// A ``Separator`` consists of an optional label. +/// +/// ```swift +/// Separator("OR", thickness: .hairline) +/// ``` /// -/// - Important: Component expands horizontally to infinity. +/// ### Customizing appearance +/// +/// The label color can be modified by ``textColor(_:)`` modifier. +/// +/// ```swift +/// Separator("OR") +/// .textColor(.blueLight) +/// ``` +/// +/// ### Layout +/// +/// Component expands horizontally. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/structure/separator/) public struct Separator: View { @Environment(\.textColor) private var textColor - let label: String - let color: Color - let thickness: CGFloat + private let label: String + private let color: Color + private let thickness: CGFloat public var body: some View { if label.isEmpty { @@ -30,12 +49,12 @@ public struct Separator: View { } } - @ViewBuilder var line: some View { + @ViewBuilder private var line: some View { color .frame(height: thickness) } - @ViewBuilder var leadingLine: some View { + @ViewBuilder private var leadingLine: some View { HStack(spacing: 0) { line LinearGradient(colors: [color, color.opacity(0)], startPoint: .leading, endPoint: .trailing) @@ -44,7 +63,7 @@ public struct Separator: View { } } - @ViewBuilder var trailingLine: some View { + @ViewBuilder private var trailingLine: some View { HStack(spacing: 0) { LinearGradient(colors: [color, color.opacity(0)], startPoint: .trailing, endPoint: .leading) .frame(width: .large) @@ -57,7 +76,7 @@ public struct Separator: View { // MARK: - Inits public extension Separator { - /// Creates Orbit Separator component. + /// Creates Orbit ``Separator`` component. init( _ label: String = "", color: Color = .cloudNormal, @@ -72,11 +91,13 @@ public extension Separator { // MARK: - Types public extension Separator { + /// Orbit ``Separator`` thickness. enum Thickness { case `default` case hairline case custom(CGFloat) + /// Value of Orbit `Separator` ``Separator/Thickness``. public var value: CGFloat { switch self { case .`default`: return 1 diff --git a/Sources/Orbit/Components/Skeleton.swift b/Sources/Orbit/Components/Skeleton.swift index 3724b8bb712..1615c985a8c 100644 --- a/Sources/Orbit/Components/Skeleton.swift +++ b/Sources/Orbit/Components/Skeleton.swift @@ -1,8 +1,18 @@ import SwiftUI -/// Shows content placeholder before data is loaded. +/// Orbit component that displays placeholder before data is loaded. +/// A counterpart of the native `redacted()` modifier. +/// +/// A ``Skeleton`` is defined by a shape that mimics the shape and size of loaded content. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/progress-indicators/skeleton/) +/// ```swift +/// Skeleton(.button) +/// Skeleton(.image) +/// Skeleton(.atomic(.rectangle), animation: animation) +/// .frame(height: 60) +/// ``` +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/progress-indicators/skeleton/) public struct Skeleton: View { public static let color: Color = .cloudNormal @@ -26,7 +36,7 @@ public struct Skeleton: View { } } - @ViewBuilder var content: some View { + @ViewBuilder private var content: some View { switch preset { case .atomic(let atomic): switch atomic { @@ -68,18 +78,34 @@ public struct Skeleton: View { } } - var roundedRectangle: RoundedRectangle { + @ViewBuilder private func line(height: CGFloat) -> some View { + roundedRectangle.fill(color).frame(height: height) + } + + private var roundedRectangle: RoundedRectangle { RoundedRectangle(cornerRadius: borderRadius) } +} - @ViewBuilder func line(height: CGFloat) -> some View { - roundedRectangle.fill(color).frame(height: height) +// MARK: - Inits +public extension Skeleton { + + /// Creates Orbit ``Skeleton`` component. + init( + _ preset: Preset, + borderRadius: CGFloat = BorderRadius.desktop, + animation: Animation = .default + ) { + self.preset = preset + self.borderRadius = borderRadius + self.animation = animation } } // MARK: - Types extension Skeleton { + /// Orbit ``Skeleton`` shape and size preset. public enum Preset { public enum Atomic { @@ -93,8 +119,13 @@ extension Skeleton { case image(height: CGFloat? = nil) case list(rows: Int, rowHeight: CGFloat = 20, spacing: CGFloat = .xSmall) case text(lines: Int, lineHeight: CGFloat = 20, spacing: CGFloat = .xSmall) + + public static let button: Self = .button() + public static let card: Self = .card() + public static let image: Self = .image() } + /// Orbit ``Skeleton`` animation. public enum Animation { case none case `default` @@ -110,21 +141,6 @@ extension Skeleton { } } -// MARK: - Inits -public extension Skeleton { - - /// Creates Orbit Skeleton component. - init( - _ preset: Preset, - borderRadius: CGFloat = BorderRadius.desktop, - animation: Animation = .default - ) { - self.preset = preset - self.borderRadius = borderRadius - self.animation = animation - } -} - // MARK: - Previews struct SkeletonPreviews: PreviewProvider { @@ -132,9 +148,9 @@ struct SkeletonPreviews: PreviewProvider { PreviewWrapper { content(animation: .none) contentAtomic(animation: .none) - Skeleton(.card(), borderRadius: BorderRadius.large, animation: .none) + Skeleton(.card, borderRadius: BorderRadius.large, animation: .none) .frame(height: 100) - Skeleton(.image(), borderRadius: BorderRadius.large, animation: .none) + Skeleton(.image, borderRadius: BorderRadius.large, animation: .none) .frame(height: 100) livePreview } @@ -164,7 +180,7 @@ struct SkeletonPreviews: PreviewProvider { Skeleton(.list(rows: 3), animation: animation) Skeleton(.image(height: 150), animation: animation) Skeleton(.card(height: 200), animation: animation) - Skeleton(.button(), animation: animation) + Skeleton(.button, animation: animation) Skeleton(.text(lines: 4), animation: animation) } } diff --git a/Sources/Orbit/Components/Slider.swift b/Sources/Orbit/Components/Slider.swift index ff2cb9c19c2..35812598e88 100644 --- a/Sources/Orbit/Components/Slider.swift +++ b/Sources/Orbit/Components/Slider.swift @@ -2,23 +2,42 @@ import SwiftUI public typealias SliderValue = BinaryFloatingPoint & Strideable & CustomStringConvertible -/// Enables selection from a range of values. +/// Orbit input component that displays a range of values. +/// A counterpart of the native `SwiftUI.Slider`. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/interaction/slider/) +/// A ``Slider`` consists of a label and two or three choices. +/// +/// ```swift +/// Slider( +/// value: $value, +/// valueLabel: value.description, +/// startLabel: "15", +/// endLabel: "200", +/// range: 15...200 +/// ) +/// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier. +/// +/// ### Layout +/// +/// Component expands horizontally. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/interaction/slider/) public struct Slider: View where Value: SliderValue, Value.Stride: BinaryFloatingPoint { - enum SelectedValue { + private enum SelectedValue { case single(Binding?) case range(Binding>?) } - @Environment(\.sizeCategory) var sizeCategory + @Environment(\.sizeCategory) private var sizeCategory - let value: SelectedValue - let valueLabel: String - let startLabel: String - let endLabel: String - let range: ClosedRange + private let value: SelectedValue + private let valueLabel: String + private let startLabel: String + private let endLabel: String + private let range: ClosedRange @State private var isFirstThumbDrag = false @State private var isSecondThumbDrag = false @@ -73,13 +92,13 @@ public struct Slider: View where Value: SliderValue, Value.Stride: Binary .accessibility(hint: .init(accessibilityHint)) } - @ViewBuilder var track: some View { + @ViewBuilder private var track: some View { RoundedRectangle(cornerRadius: lineHeight) .fill(.cloudDark) .frame(minHeight: lineHeight, maxHeight: lineHeight) } - @ViewBuilder func controls(sliderWidth: CGFloat) -> some View { + @ViewBuilder private func controls(sliderWidth: CGFloat) -> some View { let thumbDiameter = SliderThumb.circleDiameter * sizeCategory.controlRatio let thumbRadius = thumbDiameter / 2 let thumbTrackWidth = sliderWidth - thumbDiameter @@ -105,7 +124,7 @@ public struct Slider: View where Value: SliderValue, Value.Stride: Binary } } - @ViewBuilder func firstThumb(atPosition horizontalPosition: CGFloat, width: CGFloat) -> some View { + @ViewBuilder private func firstThumb(atPosition horizontalPosition: CGFloat, width: CGFloat) -> some View { thumb( horizontalPosition: horizontalPosition, width: width, @@ -116,7 +135,7 @@ public struct Slider: View where Value: SliderValue, Value.Stride: Binary .zIndex(isFirstThumbDrag ? 1 : 0) } - @ViewBuilder func secondThumb(atPosition horizontalPosition: CGFloat, width: CGFloat) -> some View { + @ViewBuilder private func secondThumb(atPosition horizontalPosition: CGFloat, width: CGFloat) -> some View { thumb( horizontalPosition: horizontalPosition, width: width, @@ -127,7 +146,7 @@ public struct Slider: View where Value: SliderValue, Value.Stride: Binary .zIndex(isSecondThumbDrag ? 1 : 0) } - @ViewBuilder func thumb( + @ViewBuilder private func thumb( horizontalPosition: CGFloat, width: CGFloat, isDragging: Binding, @@ -171,7 +190,7 @@ public struct Slider: View where Value: SliderValue, Value.Stride: Binary ) } - @ViewBuilder func strokedPath(from: CGFloat, to: CGFloat) -> some View { + @ViewBuilder private func strokedPath(from: CGFloat, to: CGFloat) -> some View { Path { path in path.move(to: .init(x: from, y: lineCenter)) path.addLine(to: .init(x: to, y: lineCenter)) @@ -215,7 +234,7 @@ public struct Slider: View where Value: SliderValue, Value.Stride: Binary // MARK: - Inits public extension Slider { - /// Creates Orbit Slider component. + /// Creates Orbit ``Slider`` component. init( value: Binding, valueLabel: String = "", @@ -239,7 +258,7 @@ public extension Slider { self.buckets = range.chunked(into: step ?? Value.Stride(range.upperBound - range.lowerBound)) } - /// Creates Orbit ranged Slider component. + /// Creates Orbit ``Slider`` component with a binding to a closed range value. init( valueRange: Binding>, valueLabel: String = "", diff --git a/Sources/Orbit/Components/SocialButton.swift b/Sources/Orbit/Components/SocialButton.swift index 0f7db5911b3..e7f265e93bc 100644 --- a/Sources/Orbit/Components/SocialButton.swift +++ b/Sources/Orbit/Components/SocialButton.swift @@ -1,12 +1,25 @@ import SwiftUI -/// Lets users sign in using a social service. +/// Orbit component that displays a control that initiates an action related to a social service. /// -/// Social buttons are designed to ease the flow for users signing in. -/// Don’t use them in any other case or in any complex scenarios. +/// A ``SocialButton`` consists of a label and predefined icon. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/socialbutton/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// ```swift +/// SocialButton("Sign in with Facebook", service: .facebook) { +/// // Tap action +/// } +/// ``` +/// +/// Before the action is triggered, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. +/// The default ``ButtonSize/regular`` size can be modified by a ``buttonSize(_:)`` modifier. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/socialbutton/) public struct SocialButton: View { @Environment(\.colorScheme) private var colorScheme @@ -40,7 +53,7 @@ public struct SocialButton: View { .backgroundStyle(background, active: backgroundActive) } - @ViewBuilder var logo: some View { + @ViewBuilder private var logo: some View { switch service { case .apple: Self.appleLogo.foregroundColor(.whiteNormal).padding(1) case .google: Self.googleLogo @@ -49,7 +62,7 @@ public struct SocialButton: View { } } - var textColor: Color { + private var textColor: Color { switch service { case .apple: return .whiteNormal case .google: return .inkDark @@ -58,7 +71,7 @@ public struct SocialButton: View { } } - var background: Color { + private var background: Color { switch service { case .apple: return colorScheme == .light ? .black : .white case .google: return .cloudNormal @@ -67,7 +80,7 @@ public struct SocialButton: View { } } - var backgroundActive: Color { + private var backgroundActive: Color { switch service { case .apple: return colorScheme == .light ? .inkNormalActive : .inkNormalActive case .google: return .cloudNormalActive @@ -80,7 +93,7 @@ public struct SocialButton: View { // MARK: - Inits public extension SocialButton { - /// Creates Orbit SocialButton component. + /// Creates Orbit ``SocialButton`` component. init(_ label: String, service: Service, action: @escaping () -> Void) { self.label = label self.service = service @@ -95,6 +108,7 @@ extension SocialButton { private static let googleLogo = Image(.google).resizable() private static let facebookLogo = Image(.facebook).resizable() + /// Orbit ``SocialButton`` social service or login method. public enum Service { case apple case google diff --git a/Sources/Orbit/Components/Stepper.swift b/Sources/Orbit/Components/Stepper.swift index 6eb75209dda..451a2070785 100644 --- a/Sources/Orbit/Components/Stepper.swift +++ b/Sources/Orbit/Components/Stepper.swift @@ -1,17 +1,29 @@ import SwiftUI -/// Enables incremental changes of a counter without a direct input. +/// Orbit input component that displays a value with and inputs to make incremental changes. +/// A counterpart of the native `SwiftUI.Stepper`. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/interaction/stepper/) +/// A ``Stepper`` consists of a value and increment and decrement buttons. +/// +/// ```swift +/// Stepper( +/// value: $value, +/// minValue: -5, +/// maxValue: 20 +/// ) +/// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/interaction/stepper/) public struct Stepper: View { - @Binding var value: Int - - let minValue: Int - let maxValue: Int - let style: Style + @Environment(\.isEnabled) private var isEnabled - @Environment(\.isEnabled) var isEnabled + @Binding private var value: Int + private let maxValue: Int + private let minValue: Int + private let style: Style public var body: some View { HStack(alignment: .center, spacing: 0) { @@ -21,14 +33,14 @@ public struct Stepper: View { } } - @ViewBuilder var valueText: some View { + @ViewBuilder private var valueText: some View { Text("\(value)") .frame(minWidth: .xMedium) .accessibility(.stepperValue) .accessibility(value: .init(value.description)) } - @ViewBuilder var decrementButton: some View { + @ViewBuilder private var decrementButton: some View { StepperButton(.minus, style: style) { value -= 1 } @@ -36,16 +48,20 @@ public struct Stepper: View { .accessibility(.stepperDecrement) } - @ViewBuilder var incrementButton: some View { + @ViewBuilder private var incrementButton: some View { StepperButton(.plus, style: style) { value += 1 } .environment(\.isEnabled, isEnabled && value < maxValue) .accessibility(.stepperIncrement) } +} + +// MARK: - Inits +public extension Stepper { - /// Creates Orbit Stepper component. - public init( + /// Creates Orbit ``Stepper`` component. + init( value: Binding, minValue: Int, maxValue: Int, @@ -61,6 +77,7 @@ public struct Stepper: View { // MARK: - Styles extension Stepper { + /// Orbit ``Stepper`` style. public enum Style { case primary case secondary diff --git a/Sources/Orbit/Components/Switch.swift b/Sources/Orbit/Components/Switch.swift index eef10d77f94..393fa91939b 100644 --- a/Sources/Orbit/Components/Switch.swift +++ b/Sources/Orbit/Components/Switch.swift @@ -1,24 +1,30 @@ import SwiftUI -/// Offers a control to toggle a setting on or off. +/// Orbit input component that displays a binary toggle control. +/// A counterpart of the native `SwiftUI.Toggle`. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/interaction/switch/) +/// ```swift +/// Switch(isOn: $isOn) +/// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/interaction/switch/) public struct Switch: View { - public static let size = CGSize(width: 50, height: 28) - public static let circleDiameter: CGFloat = 30 - public static let dotDiameter: CGFloat = 10 - - static let borderColor = Color(white: 0.2, opacity: 0.25) - static let animation = Animation.spring(response: 0.25, dampingFraction: 0.6) + private static let size = CGSize(width: 50, height: 28) + private static let circleDiameter: CGFloat = 30 + private static let dotDiameter: CGFloat = 10 + private static let borderColor = Color(white: 0.2, opacity: 0.25) + private static let animation = Animation.spring(response: 0.25, dampingFraction: 0.6) @Environment(\.sizeCategory) private var sizeCategory @Environment(\.colorScheme) private var colorScheme @Environment(\.isEnabled) private var isEnabled @Environment(\.isHapticsEnabled) private var isHapticsEnabled - @Binding var isOn: Bool - - let hasIcon: Bool + + private let hasIcon: Bool + @Binding private var isOn: Bool public var body: some View { capsule @@ -34,14 +40,14 @@ public struct Switch: View { .disabled(isEnabled == false) } - @ViewBuilder var capsule: some View { + @ViewBuilder private var capsule: some View { Capsule(style: .circular) .frame(width: width, height: height) .foregroundColor(tint) .animation(Self.animation, value: isOn) } - @ViewBuilder var indicator: some View { + @ViewBuilder private var indicator: some View { Circle() .frame(width: circleDiameter, height: circleDiameter) .foregroundColor(indicatorColor) @@ -58,7 +64,7 @@ public struct Switch: View { .animation(Self.animation, value: isOn) } - @ViewBuilder var indicatorSymbol: some View { + @ViewBuilder private var indicatorSymbol: some View { if hasIcon { Icon(isOn ? .lock : .lockOpen) .iconSize(.small) @@ -71,42 +77,46 @@ public struct Switch: View { } } - var tint: Color { + private var tint: Color { (isOn ? .blueNormal : capsuleBackgroundColor) .opacity(isEnabled ? 1 : 0.5) } - var iconTint: Color { + private var iconTint: Color { (isOn ? Color.blueNormal : Color.inkNormal) .opacity(isEnabled ? 1 : 0.5) } - var capsuleBackgroundColor: Color { + private var capsuleBackgroundColor: Color { colorScheme == .light ? .cloudDark : .cloudDark } - var indicatorColor: Color { + private var indicatorColor: Color { colorScheme == .light ? .whiteNormal : .cloudNormal } - var width: CGFloat { + private var width: CGFloat { Self.size.width * sizeCategory.controlRatio } - var height: CGFloat { + private var height: CGFloat { Self.size.height * sizeCategory.controlRatio } - var circleDiameter: CGFloat { + private var circleDiameter: CGFloat { Self.circleDiameter * sizeCategory.controlRatio } - var dotDiameter: CGFloat { + private var dotDiameter: CGFloat { Self.dotDiameter * sizeCategory.controlRatio } +} - /// Creates Orbit Switch component. - public init(isOn: Binding, hasIcon: Bool = false) { +// MARK: - Previews +public extension Switch { + + /// Creates Orbit ``Switch`` component. + init(isOn: Binding, hasIcon: Bool = false) { self._isOn = isOn self.hasIcon = hasIcon } diff --git a/Sources/Orbit/Components/Tabs.swift b/Sources/Orbit/Components/Tabs.swift index 6a067671027..e8b6bca6f4d 100644 --- a/Sources/Orbit/Components/Tabs.swift +++ b/Sources/Orbit/Components/Tabs.swift @@ -1,25 +1,40 @@ import SwiftUI -/// Separates content into groups within a single context. +/// Orbit input component that displays a control for switching between multiple contents in a group. +/// A counterpart of the native `SwiftUI.Picker` with `segmented` style applied. A typical use case is switching between tabs in a native `SwiftUI.TabView`. /// -/// The following example shows component with 3 tabs: +/// A ``Tabs`` consists of labels that represent specific tabs. +/// Each tab label must be given an Orbit `identifier` that matches a value of the selection binding: /// /// ```swift -/// var body: some View { -/// Tabs(selection: tabIndex) { -/// Text("one") -/// .identifier(1) -/// Text("two") -/// .identifier(2) -/// .activeTabStyle(.underlinedGradient(.ink)) -/// Text("three") -/// .identifier(3) -/// .activeTabStyle(.underlined(.blueNormal)) -/// } +/// Tabs(selection: $selectedTab) { +/// Text("One") +/// .identifier(1) +/// Text("Two") +/// .identifier(2) +/// .activeTabStyle(.underlinedGradient(.ink)) +/// Text("Three") +/// .identifier(3) +/// .activeTabStyle(.underlined(.blueNormal)) +/// } +/// +/// TabView(selection: $selectedTab) { +/// tabContentNumberOne +/// .tag(1) +/// tabContentNumberTwo +/// .tag(2) +/// tabContentNumberThree +/// .tag(3) /// } /// ``` +/// +/// The component can be disabled by ``disabled(_:)`` modifier. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/structure/tabs/) +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/structure/tabs/) public struct Tabs: View { @Environment(\.colorScheme) private var colorScheme diff --git a/Sources/Orbit/Components/Tag.swift b/Sources/Orbit/Components/Tag.swift index 6d786806b47..6bb14f7aa8b 100644 --- a/Sources/Orbit/Components/Tag.swift +++ b/Sources/Orbit/Components/Tag.swift @@ -1,9 +1,33 @@ import SwiftUI -/// Offers a label that can optionally be selected and unselected or removed. +/// Orbit component that displays a control that can be selected, unselected, or removed. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/tag/) -/// - Important: Component can expand horizontally by using `idealSize` modifier. +/// A ``Tag`` consists of a label, icon and optional removable action. +/// +/// ```swift +/// Tag("Sorting", icon: .sort, isSelected: $isSortingApplied) +/// Tag("Filters", style: .removable(action: { /* Tap action */ }), isSelected: $isFiltersApplied) +/// ``` +/// +/// ### Customizing appearance +/// +/// The label and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// +/// ```swift +/// Tag("Selected", isSelected: $isSelected) +/// .textColor(.blueLight) +/// .iconColor(.blueNormal) +/// ``` +/// +/// Before the action is triggered, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component does not expand horizontally, unless forced by the native ``idealSize()`` modifier with a `false` value. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/tag/) public struct Tag: View, PotentiallyEmptyView { @Environment(\.idealSize) private var idealSize @@ -61,7 +85,7 @@ public struct Tag: View, PotentiallyEmptyView { // MARK: - Inits public extension Tag { - /// Creates Orbit Tag component with custom icon. + /// Creates Orbit ``Tag`` component with custom leading icon. init( _ label: String = "", style: TagStyle = .default, @@ -76,7 +100,7 @@ public extension Tag { self.icon = icon() } - /// Creates Orbit Tag component. + /// Creates Orbit ``Tag`` component. init( _ label: String = "", icon: Icon.Symbol? = nil, @@ -98,6 +122,7 @@ public extension Tag { // MARK: - Types +/// Orbit ``Tag`` style. public enum TagStyle: Equatable { case `default` diff --git a/Sources/Orbit/Components/Text.swift b/Sources/Orbit/Components/Text.swift index 3782c448dc1..cc96c9f2def 100644 --- a/Sources/Orbit/Components/Text.swift +++ b/Sources/Orbit/Components/Text.swift @@ -1,12 +1,78 @@ import SwiftUI -/// Renders text blocks in styles to fit the purpose. +/// Orbit component that displays one or more lines of read-only text. +/// A counterpart of the native `SwiftUI.Text` with a support for Orbit markup formatting and ``TextLink``s. /// -/// A view that displays one or more lines of read-only text. Text content supports html tags ``, ``, `` for text formatting. -/// The `` and `` tags support embedding interactive ``TextLink``s withing the text. +/// A ``Text`` is created using the `String` content that can include html tags ``, ``, ``, `
` to customize formatting. +/// Interactive tags `
` and `` can be used to insert ``TextLink``s in the text. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/text/) -/// - Important: Component has fixed vertical size. When the content is empty, the component results in `EmptyView`. +/// ```swift +/// Text("Text with formatting,
multiline content and
TextLink") +/// ``` +/// +/// The Orbit ``Text`` component consists of up to 3 layers on top of each other: +/// +/// 1) A base `SwiftUI.Text` layer, either: +/// - `SwiftUI.Text(verbatim:)` (when the whole text is a plain text with no formatting) +/// - `SwiftUI.Text(attributedString:)` (when the text contains any Orbit html formatting) +/// 2) ``TextLink`` overlay (when the text contains ``TextLink``s). The native text modifier support is limited for this layer. +/// 3) A long tap gesture overlay (when the `textIsCopyable` is `true`) +/// +/// ### Customizing appearance +/// +/// Textual properties can be modified in two ways: +/// - Modifiers applied directly to `Text` that return the same type, such as ``textColor(_:)-7qk2x`` or ``textAccentColor(_:)-xmqw``. +/// These can also be used for concatenation with other Orbit textual components like ``Heading`` or ``Icon``. +/// See the `InstanceMethods` section below for full list. +/// - Modifiers applied to view hierarchy, such as ``textColor(_:)-828ud`` or ``textIsCopyable(_:)``. +/// See a full list of `View` extensions in documentation. +/// +/// ```swift +/// VStack { +/// // Will result in `inkNormal` color +/// Text("Override") +/// .textColor(.inkNormal) +/// +/// // Will result in `blueDark` color passed from environment +/// Text("Modified") +/// +/// // Will result in a default `inkDark` color, ignoring the environment value +/// Text("Default") +/// .textColor(nil) +/// +/// // Will result in mixed `redNormal` and `blueDark` colors +/// Text("Concatenated") +/// .textColor(.redNormal) +/// + Text(" Title") +/// .bold() +/// + Icon(.grid) +/// .iconColor(.redNormal) +/// } +/// .textColor(.blueDark) +/// ``` +/// +/// Formatting and behaviour for ``TextLink``s found in the text can be modified by ``textLinkAction(_:)`` and +/// ``textLinkColor(_:)`` modifiers. +/// +/// ```swift +/// Heading("Information for Passenger", type: .title3) +/// .textLinkColor(.inkNormal) +/// .textLinkAction { +/// // TextLink tap Action +/// } +/// ``` +/// +/// ### Layout +/// +/// Orbit text components use a designated vertical padding that is the same in both single and multiline variant. +/// This makes it easier to align them consistently next to other Orbit components like ``Icon`` not only based on the baseline, +/// but also by `top` or `bottom` edges, provided the component text sizes match according to Orbit rules. +/// +/// Orbit text components have a fixed vertical size. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/text/) public struct Text: View, FormattedTextBuildable, PotentiallyEmptyView { @Environment(\.multilineTextAlignment) private var multilineTextAlignment @@ -35,14 +101,6 @@ public struct Text: View, FormattedTextBuildable, PotentiallyEmptyView { var isMonospacedDigit: Bool? var lineHeight: CGFloat? - // The Orbit Text consists of up to 3 layers: - // - // 1) SwiftUI.Text base layer, either: - // - SwiftUI.Text(verbatim:) (when text is a plain text only) - // - SwiftUI.Text(attributedString:) (when text contains any Orbit html formatting) - // 2) TextLink-only overlay (when text contains any TextLink; limited native modifier support) - // 3) Long-tap-to-copy gesture overlay (when isSelectable == true) - public var body: some View { if isEmpty == false { text(textRepresentableEnvironment: textRepresentableEnvironment) @@ -249,19 +307,13 @@ public struct Text: View, FormattedTextBuildable, PotentiallyEmptyView { // MARK: - Inits public extension Text { - /// Creates Orbit Text component that displays a string literal. - /// - /// Modifiers like `textAccentColor()`, `textSize()` or `fontWeight()` can be used to further adjust the formatting. - /// To specify the formatting and behaviour for `TextLink`s found in the text, use `textLinkAction()` and - /// `textLinkColor()` modifiers. - /// - /// Use `textIsCopyable()` to allow text to react on long tap. + /// Creates Orbit ``Text`` component that displays a string literal. /// /// - Parameters: /// - content: String to display. Supports html formatting tags ``, ``, ``, `` and ``. /// - /// - Important: The text concatenation does not support interactive TextLinks. It also does not fully support some global SwiftUI native text formatting view modifiers. - /// For proper rendering of TextLinks, any required text formatting modifiers must be also called on the Text directly. + /// - Important: The text concatenation does not support interactive ``TextLink``s. It also does not fully support some global SwiftUI native text formatting view modifiers. + /// For proper rendering of `TextLinks`, any required text formatting modifiers must be also called on the `Text` directly. init(_ content: String) { self.content = content } @@ -270,7 +322,7 @@ public extension Text { // MARK: - Types public extension Text { - /// Orbit text font size. + /// Orbit predefined ``Text`` font size. enum Size: Equatable { /// 13 pts. case small @@ -291,7 +343,7 @@ public extension Text { } } - /// Text font size value. + /// Value of Orbit `Text` ``Text/Size``. public var value: CGFloat { switch self { case .small: return 13 @@ -301,7 +353,7 @@ public extension Text { } } - /// Designated line height. + /// Designated line height for Orbit `Text` ``Text/Size``. public var lineHeight: CGFloat { switch self { case .small: return 16 diff --git a/Sources/Orbit/Components/TextLink.swift b/Sources/Orbit/Components/TextLink.swift index 24530777e70..91405382045 100644 --- a/Sources/Orbit/Components/TextLink.swift +++ b/Sources/Orbit/Components/TextLink.swift @@ -1,11 +1,36 @@ import Combine import SwiftUI -/// A companion component to ``Text`` that only shows TextLinks, detected in html formatted content. +/// Orbit component that displays a one or more interactive links detected in html formatted content. +/// A counterpart of the native `SwiftUI.Link`. /// -/// The component is created automatically for all `` and `` tags found in a formatted text. +/// A ``TextLink`` is created using the `NSAttributedString` content that includes html tags `` or `` and an ``TextLink/Action`` provided by ``textLinkAction(_:)`` modifier. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/textlink/) +/// ```swift +/// Group { +/// // A standalone `TextLink` component +/// TextLink(NSAttributedString("TextLink")) +/// +/// // Orbit `Text` that contains a `TextLink` as an interactive layer over the text. +/// Text("Text with formatting,
multiline content and TextLink") +/// } +/// .textLinkAction { url, label in +/// // TextLink tap Action +/// } +/// ``` +/// +/// ### Customizing appearance +/// +/// The `TextLink` color can be modified by ``textLinkColor(_:)`` modifier. +/// +/// ```swift +/// TextLink(NSAttributedString(htmlText)) +/// .textLinkColor(.blueNormal) +/// Text(htmlText) +/// .textLinkColor(.blueNormal) +/// ``` +/// - Important: Prefer using the Orbit ``Text`` component that automatically includes the `TextLink` as a layer for all detected links. +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/textlink/) public struct TextLink: UIViewRepresentable { @Environment(\.textLinkAction) private var textLinkAction @@ -75,13 +100,12 @@ public struct TextLink: UIViewRepresentable { } // MARK: - Inits -extension TextLink { +public extension TextLink { - /// Creates an Orbit TextLink. - /// - /// The component has a size of the original text, but it only display detected links, hiding any non-link content. - /// Use `textLink(color:)` to override the TextLink colors. - public init(_ content: NSAttributedString) { + /// Creates Orbit ``TextLink`` component. + /// + /// - Important: Prefer using the Orbit ``Text`` component that automatically includes the `TextLink` as a layer for all detected links. + init(_ content: NSAttributedString) { self.content = content } } @@ -89,10 +113,10 @@ extension TextLink { // MARK: - Types public extension TextLink { - /// An action handler for a link tapped inside the ``Text`` or ``TextLink`` component. + /// An action handler for links tapped inside the Orbit ``Text`` and ``TextLink`` components. typealias Action = (URL, String) -> Void - /// Orbit TextLink color. + /// Orbit ``TextLink`` color. enum Color: Equatable { case primary case secondary diff --git a/Sources/Orbit/Components/Textarea.swift b/Sources/Orbit/Components/Textarea.swift index a76814d012a..ec3b34d1f70 100644 --- a/Sources/Orbit/Components/Textarea.swift +++ b/Sources/Orbit/Components/Textarea.swift @@ -1,9 +1,10 @@ import SwiftUI import UIKit -/// Orbit component that offers multiline text input. The component is an equivalent of the native `TextField` component with vertical axis specified. +/// Orbit input component that offers multiline text input. +/// A counterpart of the native `SwiftUI.TextField` component with `vertical` axis specified. /// -/// The Textarea is created with an optional label and a binding to a string value. +/// The ``Textarea`` is created with an optional label and a binding to a string value. /// /// ```swift /// Textarea("Label", value: $text, prompt: "Description") @@ -18,7 +19,7 @@ import UIKit /// /// ### Layout /// -/// The component expands horizontally and vertically, unless limited by a `frame` modifier. +/// The component expands horizontally and vertically. It would be typically further constrained by a `frame` modifier in vertical axis. /// /// - Note: [Orbit definition](https://orbit.kiwi/components/input/textarea/) public struct Textarea: View, TextFieldBuildable { @@ -54,7 +55,7 @@ public struct Textarea: View, TextFieldBuildable { } } - @ViewBuilder var textView: some View { + @ViewBuilder private var textView: some View { TextView( value: $value, prompt: prompt, diff --git a/Sources/Orbit/Components/Tile.swift b/Sources/Orbit/Components/Tile.swift index 22d0f6b4d52..465221f3a83 100644 --- a/Sources/Orbit/Components/Tile.swift +++ b/Sources/Orbit/Components/Tile.swift @@ -1,11 +1,42 @@ import SwiftUI -/// Groups actionable content to make it easy to scan. +/// Orbit component that displays an option to pick from a group. /// -/// Can be used standalone or wrapped inside a ``TileGroup``. +/// A ``Tile`` consists of a title, description, icon and content. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/tile/) -/// - Important: Component expands horizontally unless prevented by `fixedSize` or `idealSize` modifier. +/// ```swift +/// Tile("Profile", icon: .account) { +/// // Tap action +/// } content: { +/// // Content +/// } +/// ``` +/// +/// A `Tile` can be used standalone or wrapped in a ``TileGroup``. +/// +/// ### Customizing appearance +/// +/// The title and icon colors can be modified by ``textColor(_:)`` and ``iconColor(_:)`` modifiers. +/// The icon size can be modified by ``iconSize(custom:)`` modifier. +/// +/// The default background can be overridden by ``backgroundStyle(_:)-9odue`` modifier. +/// +/// A ``Status`` can be modified by ``status(_:)`` modifier: +/// +/// ```swift +/// Tile("Not available") { +/// // Action +/// } +/// .status(.critical) +/// ``` +/// +/// Before the action is triggered, a haptic feedback is fired via ``HapticsProvider/sendHapticFeedback(_:)``. +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` or ``idealSize()`` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/tile/) public struct Tile: View { @Environment(\.idealSize) private var idealSize @@ -40,7 +71,7 @@ public struct Tile: View { .accessibility(addTraits: .isButton) } - @ViewBuilder var buttonContent: some View { + @ViewBuilder private var buttonContent: some View { HStack(spacing: 0) { VStack(alignment: .leading, spacing: 0) { header @@ -61,7 +92,7 @@ public struct Tile: View { .overlay(separator, alignment: .init(horizontal: .listRowSeparatorLeading, vertical: .bottom)) } - @ViewBuilder var header: some View { + @ViewBuilder private var header: some View { if isHeaderEmpty == false { HStack(alignment: .top, spacing: .xSmall) { icon @@ -87,14 +118,14 @@ public struct Tile: View { } } - @ViewBuilder var customContentButtonLinkOverlay: some View { + @ViewBuilder private var customContentButtonLinkOverlay: some View { if isHeaderEmpty { inactiveButtonLink .padding(.medium) } } - @ViewBuilder var inactiveButtonLink: some View { + @ViewBuilder private var inactiveButtonLink: some View { switch disclosure { case .none, .icon: EmptyView() @@ -108,7 +139,7 @@ public struct Tile: View { } } - @ViewBuilder var disclosureIcon: some View { + @ViewBuilder private var disclosureIcon: some View { switch disclosure { case .none, .buttonLink: EmptyView() @@ -121,17 +152,17 @@ public struct Tile: View { } } - @ViewBuilder var separator: some View { + @ViewBuilder private var separator: some View { if isInsideTileGroup, isTileSeparatorVisible { Separator() } } - var tileBorderStyle: TileBorderStyle { + private var tileBorderStyle: TileBorderStyle { showBorder && isInsideTileGroup == false ? .default : .none } - var isHeaderEmpty: Bool { + private var isHeaderEmpty: Bool { title.isEmpty && description.isEmpty && icon.isEmpty } } @@ -139,7 +170,30 @@ public struct Tile: View { // MARK: - Inits public extension Tile { - /// Creates Orbit Tile component. + /// Creates Orbit ``Tile`` component with custom icon. + init( + _ title: String = "", + description: String = "", + disclosure: TileDisclosure = .icon(.chevronForward), + showBorder: Bool = true, + titleStyle: Heading.Style = .title4, + descriptionColor: Color = .inkNormal, + action: @escaping () -> Void, + @ViewBuilder content: () -> Content = { EmptyView() }, + @ViewBuilder icon: () -> Icon + ) { + self.title = title + self.description = description + self.disclosure = disclosure + self.showBorder = showBorder + self.titleStyle = titleStyle + self.descriptionColor = descriptionColor + self.action = action + self.content = content() + self.icon = icon() + } + + /// Creates Orbit ``Tile`` component. init( _ title: String = "", description: String = "", @@ -166,29 +220,6 @@ public extension Tile { Icon(icon) } } - - /// Creates Orbit Tile component with custom icon. - init( - _ title: String = "", - description: String = "", - disclosure: TileDisclosure = .icon(.chevronForward), - showBorder: Bool = true, - titleStyle: Heading.Style = .title4, - descriptionColor: Color = .inkNormal, - action: @escaping () -> Void, - @ViewBuilder content: () -> Content = { EmptyView() }, - @ViewBuilder icon: () -> Icon - ) { - self.title = title - self.description = description - self.disclosure = disclosure - self.showBorder = showBorder - self.titleStyle = titleStyle - self.descriptionColor = descriptionColor - self.action = action - self.content = content() - self.icon = icon() - } } // MARK: - Modifiers diff --git a/Sources/Orbit/Components/TileGroup.swift b/Sources/Orbit/Components/TileGroup.swift index 54696854e6f..d17f3af0e80 100644 --- a/Sources/Orbit/Components/TileGroup.swift +++ b/Sources/Orbit/Components/TileGroup.swift @@ -1,32 +1,42 @@ import SwiftUI -/// Wraps tiles to show related interactions. +/// Orbit component that wraps multiple ``Tile``s to show related interactions. /// -/// Only ``Tile``s should be used inside of a group. -/// -/// Tiles in a group show separators by default, except for the last tile, -/// which is handled automatically and never shows a separator. +/// A ``TileGroup`` consists of a set of ``Tile`` content: +/// +/// ```swift +/// TileGroup { +/// Tile("Title 1") +/// Tile("Title 2") +/// } +/// ``` /// +/// All tiles in a group include bottom separators by default, except for the last separator. /// Use ``Tile/tileSeparator(_:)`` to modify separator visibility for a given tile: /// /// ```swift /// TileGroup { -/// Tile("Title") -/// Tile("No Separator") +/// Tile("Tile with no Separator") /// .tileSeparator(false) -/// Tile("Title") +/// Tile("Tile 2") /// } /// ``` /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/tilegroup/) -public struct TileGroup: View, PotentiallyEmptyView { +/// ### Layout +/// +/// The component adds a `VStack` over the provided content. Avoid using the component when the content is large and should be embedded in a lazy stack. +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/tilegroup/) +public struct TileGroup: View, PotentiallyEmptyView { - @ViewBuilder let content: Content + @ViewBuilder private let tiles: Tiles public var body: some View { if isEmpty == false { VStack(alignment: .leading, spacing: 0) { - content + tiles .environment(\.isInsideTileGroup, true) } // hide any last separator automatically by clipping it @@ -38,12 +48,16 @@ public struct TileGroup: View, PotentiallyEmptyView { } var isEmpty: Bool { - content.isEmpty + tiles.isEmpty } +} + +// MARK: - Inits +public extension TileGroup { - /// Creates Orbit TileGroup component as a wrapper for Tile content. - public init(@ViewBuilder _ content: () -> Content) { - self.content = content() + /// Creates Orbit ``TileGroup`` component. + init(@ViewBuilder _ tiles: () -> Tiles) { + self.tiles = tiles() } } diff --git a/Sources/Orbit/Components/Timeline.swift b/Sources/Orbit/Components/Timeline.swift index 5ac6abe5f59..8f287e2c395 100644 --- a/Sources/Orbit/Components/Timeline.swift +++ b/Sources/Orbit/Components/Timeline.swift @@ -1,20 +1,31 @@ import SwiftUI -/// Shows progress on a process over time. +/// Orbit component that shows progress on a process over time. /// -/// - Related components -/// - ``TimelineItem`` +/// A ``Timeline`` consists of a set of ``TimelineItem`` content: +/// +/// ```swift +/// Timeline { +/// TimelineItem("Booked", type: .past) +/// TimelineItem("Checked in", type: .past) +/// TimelineItem("Board", type: .present) +/// } +/// ``` /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/progress-indicators/timeline/) -public struct Timeline: View, PotentiallyEmptyView { - - @Environment(\.sizeCategory) var sizeCategory - @ViewBuilder let content: Content +/// ### Layout +/// +/// When the provided content is empty, the component results in `EmptyView` so that it does not take up any space in the layout. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/progress-indicators/timeline/) +public struct Timeline: View, PotentiallyEmptyView { + @Environment(\.sizeCategory) private var sizeCategory + @ViewBuilder private let timelineItems: TimelineItems + public var body: some View { if isEmpty == false { - VStack(alignment: .leading, spacing: Self.spacing) { - content + VStack(alignment: .leading, spacing: .large) { + timelineItems } .backgroundPreferenceValue(TimelineItemPreferenceKey.self) { preferences in GeometryReader { geometry in @@ -35,29 +46,27 @@ public struct Timeline: View, PotentiallyEmptyView { } var isEmpty: Bool { - content.isEmpty + timelineItems.isEmpty } - func progressLineHeight( + private func progressLineHeight( for index: Int, in preferences: TimelineItemPreferenceKey.Value, geometry: GeometryProxy ) -> CGFloat { guard preferences.indices.contains(index) else { return 0 } - return geometry[preferences[index].bounds].height + Self.spacing - } - - /// Creates Orbit TimelineItem component. - public init(@ViewBuilder content: () -> Content) { - self.content = content() + return geometry[preferences[index].bounds].height + .large } } -// MARK: - Constants +// MARK: - Inits public extension Timeline { - static var spacing: CGFloat { .large } + /// Creates Orbit ``TimelineItem`` component. + init(@ViewBuilder _ timelineItems: () -> TimelineItems) { + self.timelineItems = timelineItems() + } } // MARK: - Previews @@ -101,7 +110,7 @@ struct TimelinePreviews: PreviewProvider { TimelineItem( "Board", sublabel: "May 4, 8:15", - type: .present(), + type: .present, description: "Be at your departure gate at least 30 minutes before boarding." ) { contentPlaceholder diff --git a/Sources/Orbit/Components/Toast.swift b/Sources/Orbit/Components/Toast.swift index e2c3668cee8..c2295ab9f2b 100644 --- a/Sources/Orbit/Components/Toast.swift +++ b/Sources/Orbit/Components/Toast.swift @@ -1,13 +1,33 @@ import Combine import SwiftUI -/// Toast shows a brief message that’s clear & understandable. +/// Orbit component that shows a brief overlay message on top of the content. +/// +/// Each displayed ``ToastWrapper`` (an interactive wrapper over ``ToastContent``) can be dismissed by a gesture. +/// +/// A ``Toast`` queue is managed by a ``ToastQueue`` that resolves what message will be displayed at which time: /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/information/toast/) -/// - Important: Component expands horizontally to infinity. +/// ```swift +/// @State var toastQueue = ToastQueue() +/// +/// var body: some View { +/// VStack { +/// // content over which to display Toasts +/// } +/// .overlay(alignment: .top) { +/// Toast(toastQueue: toastQueue) +/// } +/// } +/// ``` +/// +/// ### Layout +/// +/// Component expands horizontally unless prevented by the native `fixedSize()` modifier. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/information/toast/) public struct Toast: View { - @ObservedObject var toastQueue: ToastQueue + @ObservedObject private var toastQueue: ToastQueue public var body: some View { if let toast = toastQueue.toast { @@ -25,40 +45,21 @@ public struct Toast: View { } } - /// Creates Orbit Toast component with queue management and gesture handling. - /// - /// Apply ``Toast`` as a `.top` overlay to an existing view. - /// Provide an instance of ``ToastQueue``, which manages a queue of toasts. - /// - /// The following example shows a ``Toast`` applied to a view: - /// - /// ```swift - /// let toastQueue = ToastQueue() - /// - /// var body: some View { - /// VStack { - /// // content over which to display Toasts - /// } - /// .overlay(Toast(toastQueue: toastQueue), alignment: .top) - /// } - /// ``` - /// Alternatively: - /// - ``ToastWrapper`` can be used when gesture handling is needed, but no queue management. - /// - ``ToastContent`` when neither gesture handling or queue management is needed. + /// Creates Orbit ``Toast`` component. public init(toastQueue: ToastQueue) { self.toastQueue = toastQueue } } -/// Variant of Orbit `Toast` component with no gesture handling or queue management. +/// Orbit ``Toast`` with no gesture handling or queue management. public struct ToastContent: View { @Environment(\.colorScheme) private var colorScheme @Environment(\.textColor) private var textColor - let description: String - let icon: Icon.Symbol? - let progress: CGFloat + private let description: String + private let icon: Icon.Symbol? + private let progress: CGFloat public var body: some View { HStack(alignment: .top, spacing: .xSmall) { @@ -72,14 +73,14 @@ public struct ToastContent: View { .background(background) } - @ViewBuilder var background: some View { + @ViewBuilder private var background: some View { backgroundColor .overlay(progressIndicator, alignment: .leading) .clipShape(shape) .elevation(.level3, shape: .roundedRectangle()) } - @ViewBuilder var progressIndicator: some View { + @ViewBuilder private var progressIndicator: some View { GeometryReader { geometry in progressColor .opacity(max(0, progress * 2 - 0.5) * 0.3) @@ -89,23 +90,23 @@ public struct ToastContent: View { } } - var foregroundColor: Color { + private var foregroundColor: Color { colorScheme == .light ? .whiteNormal : .inkDark } - var backgroundColor: Color { + private var backgroundColor: Color { colorScheme == .light ? .inkDark : .whiteDarker } - var progressColor: Color { + private var progressColor: Color { colorScheme == .light ? .inkNormal : .cloudNormal } - var shape: some Shape { + private var shape: some Shape { RoundedRectangle(cornerRadius: BorderRadius.default, style: .continuous) } - /// Creates Orbit `Toast` component variant with no gesture handling or queue management. + /// Creates Orbit ``ToastContent`` component with no gesture handling or queue management. public init(_ description: String, icon: Icon.Symbol? = nil, progress: CGFloat = 0) { self.description = description self.icon = icon @@ -113,7 +114,7 @@ public struct ToastContent: View { } } -/// Variant of Orbit `Toast` component with gesture handling, but no queue management. +/// Orbit ``ToastWrapper`` component with gesture handling, but no queue management. public struct ToastWrapper: View { static let minOffsetY: CGFloat = -10 @@ -121,12 +122,12 @@ public struct ToastWrapper: View { @Environment(\.isHapticsEnabled) private var isHapticsEnabled - let description: String - let icon: Icon.Symbol? - let progress: CGFloat - let pauseAction: () -> Void - let resumeAction: () -> Void - let dismissAction: () -> Void + private let description: String + private let icon: Icon.Symbol? + private let progress: CGFloat + private let pauseAction: () -> Void + private let resumeAction: () -> Void + private let dismissAction: () -> Void @State private var offsetY: CGFloat = 0 @State private var gaveFeedback: Bool = false @@ -155,23 +156,37 @@ public struct ToastWrapper: View { ) } - var isOffsetDismiss: Bool { + private var isOffsetDismiss: Bool { offsetY < Self.minOffsetY } - var dismissProgress: CGFloat { + private var dismissProgress: CGFloat { min(0, cappedOffsetY) / Self.minOffsetY } - var opacity: CGFloat { + private var opacity: CGFloat { return 1 - dismissProgress * 0.2 } - var cappedOffsetY: CGFloat { + private var cappedOffsetY: CGFloat { min(Self.maxOffsetY, offsetY) } - - /// Creates Orbit `Toast` component variant with gesture handling. + + private func processDragChanged() { + if dismissProgress >= 1, gaveFeedback == false { + if isHapticsEnabled { + HapticsProvider.sendHapticFeedback(.notification(.warning)) + } + + gaveFeedback = true + } + + if dismissProgress == 0 { + gaveFeedback = false + } + } + + /// Creates Orbit ``ToastWrapper`` component. public init( _ description: String, icon: Icon.Symbol? = nil, @@ -188,19 +203,6 @@ public struct ToastWrapper: View { self.dismissAction = dismissAction } - private func processDragChanged() { - if dismissProgress >= 1, gaveFeedback == false { - if isHapticsEnabled { - HapticsProvider.sendHapticFeedback(.notification(.warning)) - } - - gaveFeedback = true - } - - if dismissProgress == 0 { - gaveFeedback = false - } - } } // MARK: - Previews diff --git a/Sources/Orbit/Foundation/Borders/BorderRadius.swift b/Sources/Orbit/Foundation/Borders/BorderRadius.swift index 529e1f90c1f..ad7e157144f 100644 --- a/Sources/Orbit/Foundation/Borders/BorderRadius.swift +++ b/Sources/Orbit/Foundation/Borders/BorderRadius.swift @@ -1,8 +1,8 @@ import CoreGraphics -/// Defines Orbit border radiuses. +/// Orbit predefined border radiuses. /// -/// - Note: [Orbit definition](https://orbit.kiwi/foundation/border-radiuses/) +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/foundation/border-radiuses/) public enum BorderRadius { /// 3 pts border radius. public static let desktop: CGFloat = 3 diff --git a/Sources/Orbit/Foundation/Borders/BorderWidth.swift b/Sources/Orbit/Foundation/Borders/BorderWidth.swift index b0b9a7e53cf..8b5d3d752e3 100644 --- a/Sources/Orbit/Foundation/Borders/BorderWidth.swift +++ b/Sources/Orbit/Foundation/Borders/BorderWidth.swift @@ -1,7 +1,7 @@ import CoreGraphics import UIKit -/// Defines Orbit border widths. +/// Predefiend Orbit border widths. public enum BorderWidth { /// 1 pixel border width. public static let hairline: CGFloat = 1.0 / UIScreen.main.scale diff --git a/Sources/Orbit/Foundation/Colors/Gradients.swift b/Sources/Orbit/Foundation/Colors/Gradients.swift index 16bc57eb203..6e8f43a2975 100644 --- a/Sources/Orbit/Foundation/Colors/Gradients.swift +++ b/Sources/Orbit/Foundation/Colors/Gradients.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Defines Orbit gradient styles. +/// Predefined Orbit gradient styles. public enum Gradient { case bundleBasic diff --git a/Sources/Orbit/Foundation/CountryFlags/CountryFlags+Extensions.swift b/Sources/Orbit/Foundation/CountryFlags/CountryFlags+Extensions.swift index 2bea0a76477..c10e7fc9158 100644 --- a/Sources/Orbit/Foundation/CountryFlags/CountryFlags+Extensions.swift +++ b/Sources/Orbit/Foundation/CountryFlags/CountryFlags+Extensions.swift @@ -1,6 +1,7 @@ public extension CountryFlag.CountryCode { + /// Create Orbit ``CountryCode`` from a `String`. init(_ string: String) { self = Self(rawValue: string.lowercased()) ?? .unknown } diff --git a/Sources/Orbit/Foundation/Elevations/Elevation.swift b/Sources/Orbit/Foundation/Elevations/Elevation.swift index 9eac50bb7bb..6207c7a798a 100644 --- a/Sources/Orbit/Foundation/Elevations/Elevation.swift +++ b/Sources/Orbit/Foundation/Elevations/Elevation.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Use elevation to bring content closer to users. +/// Predefined Orbit elevation that brings content closer to user. /// /// Elevation levels with higher numbers are usually visually closer to the user. public enum Elevation { @@ -11,7 +11,7 @@ public enum Elevation { case custom(opacity: Double, radius: CGFloat, x: CGFloat = 0, y: CGFloat = 0, padding: CGFloat? = nil) } -/// A shape to use as a surface on which `elevation` is applied. +/// A shape to use as a surface on which Orbit `elevation` is applied. public enum ElevationShape { /// Elevation effect shape is based on provided content. /// @@ -29,7 +29,7 @@ public enum ElevationShape { public extension View { - /// Elevates the view to make it more prominent. + /// Elevates the Orbit view to make it more prominent. /// /// Effective only in light mode. func elevation( diff --git a/Sources/Orbit/Foundation/Spacing/Spacing.swift b/Sources/Orbit/Foundation/Spacing/Spacing.swift index 186900f19da..a043f16c3cd 100644 --- a/Sources/Orbit/Foundation/Spacing/Spacing.swift +++ b/Sources/Orbit/Foundation/Spacing/Spacing.swift @@ -1,10 +1,8 @@ import SwiftUI -/// Consistent spacing makes an interface more clear and easy to scan. +/// Predefined Orbit spacing to help making interface clear and easy to scan. /// -/// Our spacing is based on a 4-pixel grid. -/// -/// - Note: [Orbit definition](https://orbit.kiwi/foundation/spacing/) +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/foundation/spacing/) public enum Spacing: CGFloat { /// 2 pts. case xxxSmall = 2 @@ -29,24 +27,24 @@ public enum Spacing: CGFloat { } public extension CGFloat { - /// 2 pts + /// Orbit 2 pts spacing. static let xxxSmall = Spacing.xxxSmall.rawValue - /// 4 pts. + /// Orbit 4 pts spacing. static let xxSmall = Spacing.xxSmall.rawValue - /// 8 pts. + /// Orbit 8 pts spacing. static let xSmall = Spacing.xSmall.rawValue - /// 12 pts. + /// Orbit 12 pts spacing. static let small = Spacing.small.rawValue - /// 16 pts. + /// Orbit 16 pts spacing. static let medium = Spacing.medium.rawValue - /// 20 pts. + /// Orbit 20 pts spacing. static let xMedium = Spacing.xMedium.rawValue - /// 24 pts. + /// Orbit 24 pts spacing. static let large = Spacing.large.rawValue - /// 32 pts. + /// Orbit 32 pts spacing. static let xLarge = Spacing.xLarge.rawValue - /// 44 pts. + /// Orbit 44 pts spacing. static let xxLarge = Spacing.xxLarge.rawValue - /// 60 pts. + /// Orbit 60 pts spacing. static let xxxLarge = Spacing.xxxLarge.rawValue } diff --git a/Sources/Orbit/Foundation/Typography/Font.swift b/Sources/Orbit/Foundation/Typography/Font.swift index 392ae353453..6be0e356cdb 100644 --- a/Sources/Orbit/Foundation/Typography/Font.swift +++ b/Sources/Orbit/Foundation/Typography/Font.swift @@ -7,10 +7,10 @@ public extension Font { static let orbitIconFontName = "orbit-icons" - /// Default ratio between font size and desired line height, used for calculating custom text sizes. + /// Default ratio between Orbit font size and desired line height, used for calculating custom text sizes. static var fontSizeToLineHeightRatio: CGFloat = 1.3333 - /// Fonts used for rendering text in Orbit. + /// Default Orbit fonts used for rendering text. static var orbitFonts: [Font.Weight: URL?] = [ .ultraLight: Bundle.orbit.url(forResource: "Circular20-Book", withExtension: "otf"), .thin: Bundle.orbit.url(forResource: "Circular20-Book", withExtension: "otf"), @@ -117,7 +117,7 @@ public extension ContentSizeCategory { } } - /// Effective size ratio for controls, based on font size ratio. + /// Effective size ratio for Orbit controls, based on font size ratio. /// The ratio is smaller than font size ratio and should be used for components or indicators that are already large enough. var controlRatio: CGFloat { 1 + (ratio - 1) * 0.5 diff --git a/Sources/Orbit/Support/Accessibility/AccessibilityID.swift b/Sources/Orbit/Support/Accessibility/AccessibilityID.swift index 5422584d2ab..4721fc81a60 100644 --- a/Sources/Orbit/Support/Accessibility/AccessibilityID.swift +++ b/Sources/Orbit/Support/Accessibility/AccessibilityID.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Orbit accessibility identifier for use in wrapper components. +/// Orbit accessibility identifier that identifies embedded sub-components. /// /// Can be extended to provide custom accessibility identifiers. public struct AccessibilityID: RawRepresentable, Equatable, Hashable { @@ -14,13 +14,13 @@ public struct AccessibilityID: RawRepresentable, Equatable, Hashable { public extension View { - /// Uses the specified identifier to identify the view inside a component. + /// Uses the specified Orbit identifier to identify the view inside a component. @available(iOS 14.0, *) func accessibilityIdentifier(_ accessibilityID: AccessibilityID) -> some View { self.accessibilityIdentifier(accessibilityID.rawValue) } - /// Uses the specified identifier to identify the view inside a component. + /// Uses the specified Orbit identifier to identify the view inside a component. @available(iOS, introduced: 13.0, deprecated: 16.1, renamed: "accessibilityIdentifier(_:)") func accessibility(_ accessibilityID: AccessibilityID) -> some View { self.accessibility(identifier: accessibilityID.rawValue) diff --git a/Sources/Orbit/Support/Builders/AlertButtonsBuilder.swift b/Sources/Orbit/Support/Builders/AlertButtonsBuilder.swift index 70f9aab3c90..ce8f5baabe4 100644 --- a/Sources/Orbit/Support/Builders/AlertButtonsBuilder.swift +++ b/Sources/Orbit/Support/Builders/AlertButtonsBuilder.swift @@ -1,6 +1,6 @@ import SwiftUI -/// A builder that constructs buttons for the ``Alert`` component. +/// A builder that constructs buttons for the Orbit ``Alert`` component. @resultBuilder public enum AlertButtonsBuilder { diff --git a/Sources/Orbit/Support/Builders/AlertInlineButtonsBuilder.swift b/Sources/Orbit/Support/Builders/AlertInlineButtonsBuilder.swift index b8f71030067..910b67dc393 100644 --- a/Sources/Orbit/Support/Builders/AlertInlineButtonsBuilder.swift +++ b/Sources/Orbit/Support/Builders/AlertInlineButtonsBuilder.swift @@ -1,6 +1,6 @@ import SwiftUI -/// A builder that constructs buttons for the ``AlertInline`` component. +/// A builder that constructs buttons for the Orbit ``AlertInline`` component. @resultBuilder public enum AlertInlineButtonsBuilder { diff --git a/Sources/Orbit/Support/ButtonStyles/AlertButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/AlertButtonStyle.swift index 578956121f6..31549141979 100644 --- a/Sources/Orbit/Support/ButtonStyles/AlertButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/AlertButtonStyle.swift @@ -26,7 +26,7 @@ public struct AlertButtonStyle: PrimitiveButtonStyle { .backgroundStyle(background, active: backgroundActive) } - var textColor: Color { + private var textColor: Color { switch (resolvedPriority, isSubtle) { case (.primary, _): return .whiteNormal case (.secondary, true): return .inkDark @@ -34,7 +34,7 @@ public struct AlertButtonStyle: PrimitiveButtonStyle { } } - var textActiveColor: Color { + private var textActiveColor: Color { switch (resolvedPriority, isSubtle) { case (.primary, _): return .whiteNormal case (.secondary, true): return .inkDarkActive @@ -42,7 +42,7 @@ public struct AlertButtonStyle: PrimitiveButtonStyle { } } - var background: Color { + private var background: Color { switch (resolvedPriority, isSubtle) { case (.primary, _): return resolvedStatus.color case (.secondary, true): return .inkDark.opacity(0.1) @@ -50,7 +50,7 @@ public struct AlertButtonStyle: PrimitiveButtonStyle { } } - var backgroundActive: Color { + private var backgroundActive: Color { switch (resolvedPriority, isSubtle) { case (.primary, _): return resolvedStatus.activeColor case (.secondary, true): return .inkDark.opacity(0.2) @@ -58,15 +58,15 @@ public struct AlertButtonStyle: PrimitiveButtonStyle { } } - var resolvedPriority: ButtonPriority { + private var resolvedPriority: ButtonPriority { buttonPriority ?? .primary } - var resolvedStatus: Status { + private var resolvedStatus: Status { status ?? .info } - /// Create button style for Orbit ``Alert`` component. + /// Creates button style for Orbit ``Alert`` component. public init() {} } diff --git a/Sources/Orbit/Support/ButtonStyles/CheckboxButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/CheckboxButtonStyle.swift index ae359ae01b6..4e16e16e168 100644 --- a/Sources/Orbit/Support/ButtonStyles/CheckboxButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/CheckboxButtonStyle.swift @@ -7,7 +7,7 @@ public struct CheckboxButtonStyle: ButtonStyle { @Environment(\.sizeCategory) private var sizeCategory @Environment(\.isEnabled) private var isEnabled - @Environment(\.textColor) var textColor + @Environment(\.textColor) private var textColor private let state: Checkbox.State private let isChecked: Bool @@ -20,7 +20,7 @@ public struct CheckboxButtonStyle: ButtonStyle { .accessibility(addTraits: isChecked ? .isSelected : []) } - func indicator(isPressed: Bool) -> some View { + private func indicator(isPressed: Bool) -> some View { shape .strokeBorder(indicatorStrokeColor(isPressed: isPressed), lineWidth: indicatorStrokeWidth) .background( @@ -50,11 +50,11 @@ public struct CheckboxButtonStyle: ButtonStyle { } } - var shape: some InsettableShape { + private var shape: some InsettableShape { RoundedRectangle(cornerRadius: BorderRadius.default * sizeCategory.controlRatio, style: .continuous) } - func indicatorStrokeColor(isPressed: Bool) -> some ShapeStyle { + private func indicatorStrokeColor(isPressed: Bool) -> some ShapeStyle { switch (isEnabled, isChecked, isPressed) { case (true, true, false): return .blueNormal case (true, true, true): return .blueLightActive @@ -62,7 +62,7 @@ public struct CheckboxButtonStyle: ButtonStyle { } } - func indicatorBackgroundColor(isPressed: Bool) -> some ShapeStyle { + private func indicatorBackgroundColor(isPressed: Bool) -> some ShapeStyle { switch (isEnabled, isChecked, isPressed) { case (true, true, false): return .blueNormal case (true, true, true): return .blueLightActive @@ -72,7 +72,7 @@ public struct CheckboxButtonStyle: ButtonStyle { } } - func indicatorOverlayStrokeColor(isPressed: Bool) -> some ShapeStyle { + private func indicatorOverlayStrokeColor(isPressed: Bool) -> some ShapeStyle { switch (state, isPressed) { case (.normal, true): return .blueNormal case (.error, true): return .redLightActive @@ -81,31 +81,31 @@ public struct CheckboxButtonStyle: ButtonStyle { } } - var labelColor: Color { + private var labelColor: Color { isEnabled ? textColor ?? .inkDark : .cloudDarkHover } - var descriptionColor: Color { + private var descriptionColor: Color { isEnabled ? .inkNormal : .cloudDarkHover } - var size: CGFloat { + private var size: CGFloat { Self.size * sizeCategory.controlRatio } - var inset: CGFloat { + private var inset: CGFloat { 0.5 * sizeCategory.controlRatio } - var errorStrokeWidth: CGFloat { + private var errorStrokeWidth: CGFloat { 3 * sizeCategory.controlRatio } - var indicatorStrokeWidth: CGFloat { + private var indicatorStrokeWidth: CGFloat { 2 * sizeCategory.controlRatio } diff --git a/Sources/Orbit/Support/ButtonStyles/ListChoiceButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/ListChoiceButtonStyle.swift index e1e75d370fd..1c8547d0317 100644 --- a/Sources/Orbit/Support/ButtonStyles/ListChoiceButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/ListChoiceButtonStyle.swift @@ -13,7 +13,7 @@ public struct ListChoiceButtonStyle: ButtonStyle { ) } - @ViewBuilder func backgroundColor(isPressed: Bool) -> some View { + @ViewBuilder private func backgroundColor(isPressed: Bool) -> some View { if isPressed { resolvedActiveBackground } else { @@ -21,7 +21,7 @@ public struct ListChoiceButtonStyle: ButtonStyle { } } - @ViewBuilder var resolvedInactiveBackground: some View { + @ViewBuilder private var resolvedInactiveBackground: some View { if let backgroundShape { backgroundShape.inactiveView } else { @@ -29,7 +29,7 @@ public struct ListChoiceButtonStyle: ButtonStyle { } } - @ViewBuilder var resolvedActiveBackground: some View { + @ViewBuilder private var resolvedActiveBackground: some View { if let backgroundShape { backgroundShape.activeView } else { diff --git a/Sources/Orbit/Support/ButtonStyles/OrbitButtonLinkButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/OrbitButtonLinkButtonStyle.swift index 011d08ea8bb..a3d7e406bc8 100644 --- a/Sources/Orbit/Support/ButtonStyles/OrbitButtonLinkButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/OrbitButtonLinkButtonStyle.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Customized button style for Orbit ``ButtonLink`` component. +/// Button style for Orbit ``ButtonLink`` component. public struct OrbitButtonLinkButtonStyle: PrimitiveButtonStyle { @Environment(\.buttonSize) private var buttonSize @@ -34,7 +34,7 @@ public struct OrbitButtonLinkButtonStyle: .idealSize(horizontal: idealSizeHorizontal, vertical: idealSize.vertical) } - var backgroundActive: Color { + private var backgroundActive: Color { switch type { case .primary: return .productLightActive case .critical: return .redLightActive @@ -42,17 +42,17 @@ public struct OrbitButtonLinkButtonStyle: } } - var resolvedButtonSize: ButtonSize { + private var resolvedButtonSize: ButtonSize { buttonSize ?? .regular } - var idealSizeHorizontal: Bool? { + private var idealSizeHorizontal: Bool? { idealSize.horizontal == false ? idealSize.horizontal : (resolvedButtonSize == .compact || idealSize.horizontal == true) } - var textColor: Color { + private var textColor: Color { switch type { case .primary: return .productNormal case .critical: return .redNormal @@ -60,7 +60,7 @@ public struct OrbitButtonLinkButtonStyle: } } - var textActiveColor: Color { + private var textActiveColor: Color { switch type { case .primary: return .productDarkActive case .critical: return .redDarkActive @@ -68,14 +68,14 @@ public struct OrbitButtonLinkButtonStyle: } } - var resolvedStatus: Status { + private var resolvedStatus: Status { switch type { case .status(let status): return status ?? self.status ?? .info default: return .info } } - var hapticFeedback: HapticsProvider.HapticFeedbackType { + private var hapticFeedback: HapticsProvider.HapticFeedbackType { switch type { case .primary: return .light(1) case .critical: return .notification(.error) @@ -83,28 +83,28 @@ public struct OrbitButtonLinkButtonStyle: } } - var horizontalPadding: CGFloat { + private var horizontalPadding: CGFloat { switch resolvedButtonSize { case .regular: return .small case .compact: return 0 } } - var verticalPadding: CGFloat { + private var verticalPadding: CGFloat { switch resolvedButtonSize { case .regular: return .small // = 44 height @ normal size case .compact: return 6 // = 32 height @ normal size } } - var horizontalBackgroundPadding: CGFloat { + private var horizontalBackgroundPadding: CGFloat { switch resolvedButtonSize { case .regular: return 0 case .compact: return .xSmall } } - var verticalBackgroundPadding: CGFloat { + private var verticalBackgroundPadding: CGFloat { switch resolvedButtonSize { case .regular: return 0 case .compact: return .xxxSmall diff --git a/Sources/Orbit/Support/ButtonStyles/OrbitButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/OrbitButtonStyle.swift index df8df5e7c5f..617a3c42477 100644 --- a/Sources/Orbit/Support/ButtonStyles/OrbitButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/OrbitButtonStyle.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Button style for Orbit ``Button`` component. +/// Button style for Orbit primary ``Button`` component. /// /// The style can be further customized by using Orbit environment modifiers. public struct OrbitButtonStyle: PrimitiveButtonStyle { @@ -31,7 +31,7 @@ public struct OrbitButtonStyle: Primitive .backgroundStyle(backgroundShape?.inactive ?? background, active: backgroundShape?.active ?? backgroundActive) } - var background: Color { + private var background: Color { switch type { case .primary: return .productNormal case .primarySubtle: return .productLight @@ -43,7 +43,7 @@ public struct OrbitButtonStyle: Primitive } } - var backgroundActive: Color { + private var backgroundActive: Color { switch type { case .primary: return .productNormalActive case .primarySubtle: return .productLightActive @@ -55,7 +55,7 @@ public struct OrbitButtonStyle: Primitive } } - var labelColor: Color { + private var labelColor: Color { switch type { case .primary: return .whiteNormal case .primarySubtle: return .productDark @@ -67,18 +67,18 @@ public struct OrbitButtonStyle: Primitive } } - var resolvedStatus: Status { + private var resolvedStatus: Status { switch type { case .status(let status, _): return status ?? self.status ?? .info default: return .info } } - var resolvedButtonSize: ButtonSize { + private var resolvedButtonSize: ButtonSize { buttonSize ?? .regular } - var hapticFeedback: HapticsProvider.HapticFeedbackType { + private var hapticFeedback: HapticsProvider.HapticFeedbackType { switch type { case .primary: return .light(1) case .primarySubtle, .secondary: return .light(0.5) @@ -87,14 +87,14 @@ public struct OrbitButtonStyle: Primitive } } - var textSize: Text.Size { + private var textSize: Text.Size { switch resolvedButtonSize { case .regular: return .normal case .compact: return .small } } - var padding: CGFloat { + private var padding: CGFloat { switch resolvedButtonSize { case .regular: return .small // = 44 height @ normal size case .compact: return .xSmall // = 32 height @ normal size diff --git a/Sources/Orbit/Support/ButtonStyles/RadioButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/RadioButtonStyle.swift index 4bf548e9a6c..a44c3ff2e9d 100644 --- a/Sources/Orbit/Support/ButtonStyles/RadioButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/RadioButtonStyle.swift @@ -19,7 +19,7 @@ public struct RadioButtonStyle: ButtonStyle { .accessibility(addTraits: isChecked ? .isSelected : []) } - func indicator(isPressed: Bool) -> some View { + private func indicator(isPressed: Bool) -> some View { indicatorShape .strokeBorder(indicatorStrokeColor(isPressed: isPressed), lineWidth: strokeWidth) .background( @@ -43,11 +43,11 @@ public struct RadioButtonStyle: ButtonStyle { } } - var indicatorShape: some InsettableShape { + private var indicatorShape: some InsettableShape { Circle() } - func indicatorStrokeColor(isPressed: Bool) -> some ShapeStyle { + private func indicatorStrokeColor(isPressed: Bool) -> some ShapeStyle { switch (isEnabled, isChecked, isPressed) { case (true, true, false): return .blueNormal case (true, true, true): return .blueLightActive @@ -55,14 +55,14 @@ public struct RadioButtonStyle: ButtonStyle { } } - var indicatorBackgroundColor: some ShapeStyle { + private var indicatorBackgroundColor: some ShapeStyle { switch (isEnabled, isChecked) { case (false, false): return .cloudNormal case (_, _): return .clear } } - func indicatorOverlayStrokeColor(isPressed: Bool) -> some ShapeStyle { + private func indicatorOverlayStrokeColor(isPressed: Bool) -> some ShapeStyle { switch (state, isPressed) { case (.normal, true): return .blueNormal case (.error, true): return .redLightActive @@ -71,19 +71,19 @@ public struct RadioButtonStyle: ButtonStyle { } } - var size: CGFloat { + private var size: CGFloat { Self.size * sizeCategory.controlRatio } - var strokeWidth: CGFloat { + private var strokeWidth: CGFloat { (isChecked ? 6 : 2) * sizeCategory.controlRatio } - var errorStrokeWidth: CGFloat { + private var errorStrokeWidth: CGFloat { 3 * sizeCategory.controlRatio } - var indicatorStrokeWidth: CGFloat { + private var indicatorStrokeWidth: CGFloat { 2 * sizeCategory.controlRatio } diff --git a/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift index 19300120e75..30deb05a306 100644 --- a/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift @@ -39,7 +39,7 @@ public struct TagButtonStyle: ButtonStyle { .cornerRadius(BorderRadius.default) } - @ViewBuilder func resolvedBackgroundColor(isPressed: Bool) -> some View { + @ViewBuilder private func resolvedBackgroundColor(isPressed: Bool) -> some View { if isPressed { resolvedActiveBackground } else { @@ -47,7 +47,7 @@ public struct TagButtonStyle: ButtonStyle { } } - @ViewBuilder var resolvedInactiveBackground: some View { + @ViewBuilder private var resolvedInactiveBackground: some View { if let backgroundShape { backgroundShape.inactiveView } else { @@ -55,7 +55,7 @@ public struct TagButtonStyle: ButtonStyle { } } - @ViewBuilder var resolvedActiveBackground: some View { + @ViewBuilder private var resolvedActiveBackground: some View { if let backgroundShape { backgroundShape.activeView } else { @@ -63,7 +63,7 @@ public struct TagButtonStyle: ButtonStyle { } } - var inactiveBackgroundColor: Color { + private var inactiveBackgroundColor: Color { switch (isFocused, isSelected) { case (true, false): return .blueLight case (true, true): return .blueNormal @@ -72,7 +72,7 @@ public struct TagButtonStyle: ButtonStyle { } } - var activeBackgroundColor: Color { + private var activeBackgroundColor: Color { switch (isFocused, isSelected) { case (true, false): return .blueLightActive case (true, true): return .blueNormalActive @@ -81,11 +81,11 @@ public struct TagButtonStyle: ButtonStyle { } } - var resolvedTextColor: Color { + private var resolvedTextColor: Color { textColor ?? labelColor } - var labelColor: Color { + private var labelColor: Color { switch (isFocused, isSelected) { case (_, true): return .whiteNormal case (true, false): return .blueDarker @@ -93,11 +93,11 @@ public struct TagButtonStyle: ButtonStyle { } } - func resolvedIconColor(isPressed: Bool) -> Color { + private func resolvedIconColor(isPressed: Bool) -> Color { iconColor ?? iconColor(isPressed: isPressed) } - func iconColor(isPressed: Bool) -> Color { + private func iconColor(isPressed: Bool) -> Color { switch (isSelected, isFocused, isPressed) { case (true, _, _): return .whiteNormal case (false, true, false): return .blueDarker.opacity(0.3) diff --git a/Sources/Orbit/Support/ButtonStyles/TileButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/TileButtonStyle.swift index e3c22da42b1..0ae9a56e38a 100644 --- a/Sources/Orbit/Support/ButtonStyles/TileButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/TileButtonStyle.swift @@ -16,7 +16,7 @@ public struct TileButtonStyle: ButtonStyle { .tileBorder(style, isSelected: isSelected) } - @ViewBuilder func backgroundColor(isPressed: Bool) -> some View { + @ViewBuilder private func backgroundColor(isPressed: Bool) -> some View { if isPressed { resolvedActiveBackground } else { @@ -24,7 +24,7 @@ public struct TileButtonStyle: ButtonStyle { } } - @ViewBuilder var resolvedInactiveBackground: some View { + @ViewBuilder private var resolvedInactiveBackground: some View { if let backgroundShape { backgroundShape.inactiveView } else { @@ -32,7 +32,7 @@ public struct TileButtonStyle: ButtonStyle { } } - @ViewBuilder var resolvedActiveBackground: some View { + @ViewBuilder private var resolvedActiveBackground: some View { if let backgroundShape { backgroundShape.activeView } else { diff --git a/Sources/Orbit/Support/Components/BarButton.swift b/Sources/Orbit/Support/Components/BarButton.swift index 62da5f04317..f3cf4da328a 100644 --- a/Sources/Orbit/Support/Components/BarButton.swift +++ b/Sources/Orbit/Support/Components/BarButton.swift @@ -1,6 +1,6 @@ import SwiftUI -/// An icon-based bar button for suitable for actions inside toolbar or navigation bar. +/// Orbit support component that displays icon-based button suitable for actions in a toolbar or navigation bar. public struct BarButton: View { @Environment(\.iconSize) private var iconSize @@ -29,11 +29,11 @@ public struct BarButton: View { .buttonStyle(IconButtonStyle()) } - var resolvedIconSize: CGFloat { + private var resolvedIconSize: CGFloat { iconSize ?? Orbit.Icon.Size.large.value } - var horizontalEdges: Edge.Set { + private var horizontalEdges: Edge.Set { switch alignment { case .leading: return .trailing case .trailing: return .leading @@ -41,7 +41,7 @@ public struct BarButton: View { } } - /// Creates Orbit BarButton component. + /// Creates Orbit ``BarButton`` component. public init( _ icon: Icon.Symbol, alignment: HorizontalAlignment = .center, @@ -54,7 +54,7 @@ public struct BarButton: View { } } - /// Creates Orbit BarButton component with custom icon. + /// Creates Orbit ``BarButton`` component with custom icon. public init( alignment: HorizontalAlignment = .center, action: @escaping () -> Void, diff --git a/Sources/Orbit/Support/Components/IconButton.swift b/Sources/Orbit/Support/Components/IconButton.swift index 230b3b6ef0a..0c233762223 100644 --- a/Sources/Orbit/Support/Components/IconButton.swift +++ b/Sources/Orbit/Support/Components/IconButton.swift @@ -1,6 +1,6 @@ import SwiftUI -/// An icon-based button. +/// Orbit support component that displays an icon-based button. public struct IconButton: View { @Environment(\.isHapticsEnabled) private var isHapticsEnabled @@ -27,7 +27,7 @@ public struct IconButton: View { // MARK: - Icons public extension IconButton { - /// Creates Orbit IconButton component with custom icon. + /// Creates Orbit ``IconButton`` component with custom icon. init( action: @escaping () -> Void, @ViewBuilder icon: () -> Icon @@ -36,7 +36,7 @@ public extension IconButton { self.icon = icon() } - /// Creates Orbit IconButton component. + /// Creates Orbit ``IconButton`` component. init( _ icon: Orbit.Icon.Symbol, action: @escaping () -> Void diff --git a/Sources/Orbit/Support/Components/ListItem.swift b/Sources/Orbit/Support/Components/ListItem.swift index 63174972854..5876b98c04d 100644 --- a/Sources/Orbit/Support/Components/ListItem.swift +++ b/Sources/Orbit/Support/Components/ListItem.swift @@ -1,11 +1,17 @@ import SwiftUI -/// One item in Orbit List component. +/// Orbit component that displays one item in Orbit ``List``. /// -/// - Related components -/// - ``List`` +/// A ``ListItem`` consists of a label and icon. /// -/// - [Orbit](https://orbit.kiwi/components/structure/list/) +/// ```swift +/// List { +/// ListItem("Planes", icon: .airplane) +/// ListItem("Trains") +/// } +/// ``` +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/structure/list/) public struct ListItem: View { @Environment(\.sizeCategory) private var sizeCategory @@ -33,7 +39,7 @@ public struct ListItem: View { // MARK: - Inits public extension ListItem { - /// Creates Orbit ListItem component. + /// Creates Orbit ``ListItem`` component. init( _ text: String = "", icon: Icon.Symbol? = .circleSmall, @@ -47,7 +53,7 @@ public extension ListItem { } } - /// Creates Orbit ListItem component. + /// Creates Orbit ``ListItem`` component with custom icon. init( _ text: String = "", type: ListItemType = .primary, @@ -61,6 +67,7 @@ public extension ListItem { // MARK: - Types +/// Orbit ``ListItem`` type. public enum ListItemType { case primary diff --git a/Sources/Orbit/Support/Components/NavigationButton.swift b/Sources/Orbit/Support/Components/NavigationButton.swift index 19b859c5fb1..a747da67d2e 100644 --- a/Sources/Orbit/Support/Components/NavigationButton.swift +++ b/Sources/Orbit/Support/Components/NavigationButton.swift @@ -1,6 +1,6 @@ import SwiftUI -/// A special case of ``BarButton`` suitable for main navigation actions inside toolbar or navigation bar. +/// Orbit support component that is a special case of ``BarButton``, suitable for main navigation actions inside toolbar or navigation bar. public struct NavigationButton: View { @Environment(\.isHapticsEnabled) private var isHapticsEnabled @@ -21,6 +21,7 @@ public struct NavigationButton: View { .buttonStyle(NavigationButtonStyle()) } + /// Creates Orbit ``NavigationButton`` component. public init(_ state: State, action: @escaping () -> Void) { self.state = state self.action = action @@ -30,6 +31,7 @@ public struct NavigationButton: View { // MARK: - Types extension NavigationButton { + /// Orbit ``NavigationButton`` state. public enum State { case back case close diff --git a/Sources/Orbit/Support/Components/PasswordStrengthIndicator.swift b/Sources/Orbit/Support/Components/PasswordStrengthIndicator.swift index ff00b1fa8af..650862b0bfd 100644 --- a/Sources/Orbit/Support/Components/PasswordStrengthIndicator.swift +++ b/Sources/Orbit/Support/Components/PasswordStrengthIndicator.swift @@ -1,9 +1,9 @@ import SwiftUI -/// Orbit password strength indicator. +/// Orbit support component that displays password strength indicator. public struct PasswordStrengthIndicator: View { - let passwordStrength: PasswordStrength + private let passwordStrength: PasswordStrength public var body: some View { if text.isEmpty == false { @@ -20,7 +20,7 @@ public struct PasswordStrengthIndicator: View { } } - var indicator: some View { + private var indicator: some View { Capsule() .fill(.cloudNormal) .overlay(bar) @@ -28,7 +28,7 @@ public struct PasswordStrengthIndicator: View { .frame(maxWidth: .infinity, alignment: .leading) } - var bar: some View { + private var bar: some View { HStack(spacing: 0) { Capsule() .fill(color) @@ -41,7 +41,7 @@ public struct PasswordStrengthIndicator: View { } } - var text: String { + private var text: String { switch passwordStrength { case .weak(let title): return title case .medium(let title): return title @@ -49,7 +49,7 @@ public struct PasswordStrengthIndicator: View { } } - var spacers: Int { + private var spacers: Int { switch passwordStrength { case .weak: return 3 case .medium: return 1 @@ -57,7 +57,7 @@ public struct PasswordStrengthIndicator: View { } } - var color: Color { + private var color: Color { switch passwordStrength { case .weak: return .redNormal case .medium: return .orangeNormal @@ -65,6 +65,7 @@ public struct PasswordStrengthIndicator: View { } } + /// Creates Orbit ``PasswordStrengthIndicator`` component. public init(passwordStrength: PasswordStrengthIndicator.PasswordStrength) { self.passwordStrength = passwordStrength } @@ -73,6 +74,7 @@ public struct PasswordStrengthIndicator: View { // MARK: - Types public extension PasswordStrengthIndicator { + /// Orbit ``PasswordStrengthIndicator`` strength. enum PasswordStrength: Equatable { case weak(title: String) case medium(title: String) diff --git a/Sources/Orbit/Support/Components/Progress.swift b/Sources/Orbit/Support/Components/Progress.swift index bc24ab14ba3..3e9543d8eac 100644 --- a/Sources/Orbit/Support/Components/Progress.swift +++ b/Sources/Orbit/Support/Components/Progress.swift @@ -1,5 +1,6 @@ import SwiftUI +/// Orbit support component that displays progress. public struct Progress: View { public static let height: CGFloat = 5 @@ -20,6 +21,7 @@ public struct Progress: View { ) } + /// Creates Orbit ``Progress`` component. public init(_ progress: CGFloat) { self.progress = progress } diff --git a/Sources/Orbit/Support/Components/SliderThumb.swift b/Sources/Orbit/Support/Components/SliderThumb.swift index 8833f273fa0..bec41aa0602 100644 --- a/Sources/Orbit/Support/Components/SliderThumb.swift +++ b/Sources/Orbit/Support/Components/SliderThumb.swift @@ -7,7 +7,7 @@ struct SliderThumb: View { public static let circleDiameter: CGFloat = 24 public static let dotDiameter: CGFloat = 8 - public var body: some View { + var body: some View { Circle() .frame(width: circleDiameter, height: circleDiameter) .foregroundColor(.white) diff --git a/Sources/Orbit/Support/Components/StepperButton.swift b/Sources/Orbit/Support/Components/StepperButton.swift index 5a19539b482..29993f5670e 100644 --- a/Sources/Orbit/Support/Components/StepperButton.swift +++ b/Sources/Orbit/Support/Components/StepperButton.swift @@ -1,14 +1,14 @@ import SwiftUI -/// An icon-based stepper button for suitable for actions inside components like `Stepper`. +/// Orbit support component that displays icon-based stepper button for suitable for actions inside components like ``Stepper``. public struct StepperButton: View { - @Environment(\.isEnabled) var isEnabled - @Environment(\.sizeCategory) var sizeCategory + @Environment(\.isEnabled) private var isEnabled + @Environment(\.sizeCategory) private var sizeCategory - let icon: Icon.Symbol - let style: Stepper.Style - let action: () -> Void + private let icon: Icon.Symbol + private let style: Stepper.Style + private let action: () -> Void public var body: some View { SwiftUI.Button { @@ -21,11 +21,11 @@ public struct StepperButton: View { .buttonStyle(OrbitStyle(style: style)) } - var color: Color { + private var color: Color { isEnabled ? style.textActiveColor : style.textColor } - var size: CGFloat { + private var size: CGFloat { .xxLarge * sizeCategory.controlRatio } } @@ -33,7 +33,7 @@ public struct StepperButton: View { // MARK: - Inits extension StepperButton { - /// Creates Orbit StepperButton component used in a Stepper. + /// Creates Orbit ``StepperButton`` component used in a ``Stepper``. public init( _ icon: Icon.Symbol, style: Stepper.Style = .primary, diff --git a/Sources/Orbit/Support/Components/TabStyle.swift b/Sources/Orbit/Support/Components/TabStyle.swift index 108482ecd51..ca94b68b4f1 100644 --- a/Sources/Orbit/Support/Components/TabStyle.swift +++ b/Sources/Orbit/Support/Components/TabStyle.swift @@ -1,8 +1,8 @@ import SwiftUI -/// Style applied to the active tab in ``Tabs``. +/// Orbit ``Tabs`` style applied to the active tab. /// -/// To apply a style, use the `.activeTabStyle` modifier. +/// To apply a style, use the `activeTabStyle()` modifier. public enum TabStyle: Equatable { case `default` case underlined(Color) @@ -30,7 +30,7 @@ public enum TabStyle: Equatable { public extension View { - /// Applies the given style to the active tab in ``Tabs``. + /// Applies the given style to the active tab in Orbit ``Tabs``. func activeTabStyle(_ style: TabStyle) -> some View { modifier(ActiveTabStyleModifier(style: style)) } diff --git a/Sources/Orbit/Support/Components/TileBorder.swift b/Sources/Orbit/Support/Components/TileBorder.swift index 781e989d9e4..ab557dbf2f4 100644 --- a/Sources/Orbit/Support/Components/TileBorder.swift +++ b/Sources/Orbit/Support/Components/TileBorder.swift @@ -1,5 +1,6 @@ import SwiftUI +/// Predefined Orbit border styles for ``Tile``-like components. public enum TileBorderStyle { case none case `default` @@ -9,16 +10,16 @@ public enum TileBorderStyle { case plain } -/// Provides decoration with ``Tile`` appearance. +/// Provides decoration using Orbit ``Tile`` appearance. public struct TileBorderModifier: ViewModifier { static let animation: Animation = .easeIn(duration: 0.15) @Environment(\.horizontalSizeClass) private var horizontalSizeClass - @Environment(\.status) var status + @Environment(\.status) private var status - let style: TileBorderStyle - let isSelected: Bool + private let style: TileBorderStyle + private let isSelected: Bool public func body(content: Content) -> some View { content @@ -28,7 +29,7 @@ public struct TileBorderModifier: ViewModifier { .overlay(border.animation(Self.animation, value: isSelected)) } - @ViewBuilder var border: some View { + @ViewBuilder private var border: some View { switch (style, horizontalSizeClass) { case (.none, _): EmptyView() @@ -45,20 +46,20 @@ public struct TileBorderModifier: ViewModifier { } } - @ViewBuilder var compactSeparatorBorder: some View { + @ViewBuilder private var compactSeparatorBorder: some View { borderColor .frame(height: status == nil ? 1 : BorderWidth.active) } - @ViewBuilder var clipShape: some InsettableShape { + @ViewBuilder private var clipShape: some InsettableShape { RoundedRectangle(cornerRadius: cornerRadius) } - var isCompact: Bool { + private var isCompact: Bool { (style == .iOS) && horizontalSizeClass == .compact } - var cornerRadius: CGFloat { + private var cornerRadius: CGFloat { switch (style, horizontalSizeClass) { case (.default, _): return BorderRadius.default case (.plain, _): return BorderRadius.default @@ -68,7 +69,7 @@ public struct TileBorderModifier: ViewModifier { } } - var elevation: Elevation? { + private var elevation: Elevation? { guard status == .none else { return nil } @@ -81,13 +82,13 @@ public struct TileBorderModifier: ViewModifier { } } - var borderWidth: CGFloat { + private var borderWidth: CGFloat { isSelected || status != nil ? BorderWidth.active : 1 } - var borderColor: Color { + private var borderColor: Color { if let status = status { return status.color } @@ -99,17 +100,23 @@ public struct TileBorderModifier: ViewModifier { return showOuterBorder ? .cloudNormal : .clear } - var showOuterBorder: Bool { + private var showOuterBorder: Bool { switch style { case .iOS, .plain: return true case .none, .default: return false } } + + /// Creates Orbit ``TileBorderModifier``. + public init(style: TileBorderStyle, isSelected: Bool) { + self.style = style + self.isSelected = isSelected + } } public extension View { - /// Decorates content with a border similar to ``Tile`` or ``Card`` appearance using specified style. + /// Decorates content with Orbit border similar to ``Tile`` or ``Card`` appearance using specified style. func tileBorder( _ style: TileBorderStyle = .default, isSelected: Bool = false diff --git a/Sources/Orbit/Support/Components/TimelineItem.swift b/Sources/Orbit/Support/Components/TimelineItem.swift index 00f6cd62966..8316a2edbeb 100644 --- a/Sources/Orbit/Support/Components/TimelineItem.swift +++ b/Sources/Orbit/Support/Components/TimelineItem.swift @@ -1,21 +1,28 @@ import SwiftUI -/// One item of a Timeline. +/// Orbit component that shows a single item in a ``Timeline``. /// -/// - Related components -/// - ``Timeline`` +/// A ``TimelineItem`` consists of a label, description and a type that determines its visual progress: +/// +/// ```swift +/// Timeline { +/// TimelineItem("Booked", type: .past) +/// TimelineItem("Checked in", type: .past) +/// TimelineItem("Board", type: .present) +/// } +/// ``` /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/progress-indicators/timeline/) +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/progress-indicators/timeline/) public struct TimelineItem: View { - @Environment(\.sizeCategory) var sizeCategory - @Environment(\.horizontalSizeClass) var horizontalSizeClass + @Environment(\.sizeCategory) private var sizeCategory + @Environment(\.horizontalSizeClass) private var horizontalSizeClass - let label: String - let sublabel: String - let description: String - let type: TimelineItemType - @ViewBuilder let footer: Footer + private let label: String + private let sublabel: String + private let description: String + private let type: TimelineItemType + @ViewBuilder private let footer: Footer public var body: some View { HStack(alignment: alignment, spacing: .small) { @@ -36,7 +43,7 @@ public struct TimelineItem: View { } } - @ViewBuilder var header: some View { + @ViewBuilder private var header: some View { if horizontalSizeClass == .compact && sizeCategory.isAccessibilitySize { VStack(alignment: .leading, spacing: .xSmall) { headerContent @@ -48,21 +55,21 @@ public struct TimelineItem: View { } } - @ViewBuilder var headerContent: some View { + @ViewBuilder private var headerContent: some View { Heading(label, style: .title5) Text(sublabel) .textSize(.small) } - var alignment: VerticalAlignment { + private var alignment: VerticalAlignment { hasHeaderContent || hasDescription ? .firstTextBaseline : .top } - var hasHeaderContent: Bool { + private var hasHeaderContent: Bool { label.isEmpty == false || sublabel.isEmpty == false } - var hasDescription: Bool { + private var hasDescription: Bool { description.isEmpty == false } } @@ -71,7 +78,7 @@ public struct TimelineItem: View { public extension TimelineItem { - /// Creates Orbit TimelineItem component with text details and optional custom content at the bottom. + /// Creates Orbit ``TimelineItem`` component. init( _ label: String = "", sublabel: String = "", @@ -79,7 +86,6 @@ public extension TimelineItem { description: String = "", @ViewBuilder footer: () -> Footer = { EmptyView() } ) { - self.label = label self.sublabel = sublabel self.type = type @@ -90,8 +96,10 @@ public extension TimelineItem { // MARK: - Types +/// Orbit ``TimelineItem`` type. public enum TimelineItemType: Equatable { + /// Orbit ``TimelineItemType`` status. public enum Status { case success case warning @@ -102,6 +110,8 @@ public enum TimelineItemType: Equatable { case present(Status? = nil) case future + public static var present: Self { present() } + public var icon: Icon.Symbol? { switch self { case .past: return .checkCircle diff --git a/Sources/Orbit/Support/Environment Keys/BackgroundShapeKey.swift b/Sources/Orbit/Support/Environment Keys/BackgroundShapeKey.swift index c4359f120f6..e1273a8761c 100644 --- a/Sources/Orbit/Support/Environment Keys/BackgroundShapeKey.swift +++ b/Sources/Orbit/Support/Environment Keys/BackgroundShapeKey.swift @@ -44,18 +44,18 @@ public extension View { /// Set the inactive and active background shape styles for supported Orbit components within the view hierarchy. /// - /// To restore the default background style, set the `backgroundShape` environment value to nil using the environment(_:_:) modifer. + /// To restore the default background style, set the `backgroundShape` environment value to nil using the `environment(_:_:)` modifer. /// /// - Parameters: /// - shape: A `Color` or a `LinearGradient` that will be used in supported Orbit components such as `Card` or `Badge` as a background style. - /// - shape: A `Color` or a `LinearGradient` that will be used in supported touchable Orbit components such as `Tile` as inactive and active background style. + /// - active: A `Color` or a `LinearGradient` that will be used in supported touchable Orbit components such as `Tile` as active background style. func backgroundStyle(_ shape: any ShapeStyle, active: any ShapeStyle) -> some View { environment(\.backgroundShape, .init(inactive: shape, active: active)) } /// Set the background shape style for supported Orbit components within the view hierarchy. /// - /// To restore the default background style, set the `backgroundShape` environment value to nil using the environment(_:_:) modifer. + /// To restore the default background style, set the `backgroundShape` environment value to nil using the `environment(_:_:)` modifer. /// /// - Parameters: /// - shape: A `Color` or a `LinearGradient` that will be used in supported Orbit components such as `Card` or `Badge` as a background style. diff --git a/Sources/Orbit/Support/Environment Keys/ButtonSizeKey.swift b/Sources/Orbit/Support/Environment Keys/ButtonSizeKey.swift index 6eb3ebfc62d..54ca4dd8b6c 100644 --- a/Sources/Orbit/Support/Environment Keys/ButtonSizeKey.swift +++ b/Sources/Orbit/Support/Environment Keys/ButtonSizeKey.swift @@ -12,7 +12,7 @@ struct ButtonSizeKey: EnvironmentKey { public extension EnvironmentValues { - /// An Orbit `ButtonSize` value stored in a view’s environment. + /// An Orbit ``ButtonSize`` value stored in a view’s environment. var buttonSize: ButtonSize? { get { self[ButtonSizeKey.self] } set { self[ButtonSizeKey.self] = newValue } @@ -21,7 +21,7 @@ public extension EnvironmentValues { public extension View { - /// Set the button size for this view. + /// Set the Orbit ``ButtonSize`` for this view. /// /// - Parameters: /// - size: A button size that will be used by all components in the view hierarchy. diff --git a/Sources/Orbit/Support/Environment Keys/HapticsEnabledKey.swift b/Sources/Orbit/Support/Environment Keys/HapticsEnabledKey.swift index 23e28a1e850..21f8cf5e067 100644 --- a/Sources/Orbit/Support/Environment Keys/HapticsEnabledKey.swift +++ b/Sources/Orbit/Support/Environment Keys/HapticsEnabledKey.swift @@ -6,7 +6,7 @@ struct HapticsEnabledKey: EnvironmentKey { public extension EnvironmentValues { - /// Whether haptic feedback on controls is enabled. + /// Whether Orbit haptic feedback on controls is enabled. var isHapticsEnabled: Bool { get { self[HapticsEnabledKey.self] } set { self[HapticsEnabledKey.self] = newValue } @@ -15,7 +15,7 @@ public extension EnvironmentValues { public extension View { - /// Disables haptic feedback on controls. + /// Disables Orbit haptic feedback on controls. func hapticsDisabled(_ disabled: Bool = true) -> some View { environment(\.isHapticsEnabled, disabled == false) } diff --git a/Sources/Orbit/Support/Environment Keys/IconColorKey.swift b/Sources/Orbit/Support/Environment Keys/IconColorKey.swift index a328617ebc0..cc4e78385fb 100644 --- a/Sources/Orbit/Support/Environment Keys/IconColorKey.swift +++ b/Sources/Orbit/Support/Environment Keys/IconColorKey.swift @@ -6,7 +6,7 @@ struct IconColorKey: EnvironmentKey { public extension EnvironmentValues { - /// An icon color stored in a view’s environment, used for Orbit Icon component. + /// An icon color stored in a view’s environment, used for Orbit ``Icon`` component. /// /// This environment value serves as a replacement for non-public `foregroundColor` environment value. /// This value has a priority over a `textColor`. @@ -18,7 +18,7 @@ public extension EnvironmentValues { public extension View { - /// Set the color for any Orbit `Icon` component within the view hierarchy. + /// Set the color for any Orbit ``Icon`` component within the view hierarchy. /// /// The `iconColor` value has a priority over the `textColor` environment value. /// diff --git a/Sources/Orbit/Support/Environment Keys/IconSizeKey.swift b/Sources/Orbit/Support/Environment Keys/IconSizeKey.swift index 6daf2707eb5..70f37b6d7a4 100644 --- a/Sources/Orbit/Support/Environment Keys/IconSizeKey.swift +++ b/Sources/Orbit/Support/Environment Keys/IconSizeKey.swift @@ -6,7 +6,7 @@ struct IconSizeKey: EnvironmentKey { public extension EnvironmentValues { - /// An icon size stored in a view’s environment, used for Orbit Icon component. + /// An icon size stored in a view’s environment, used for Orbit ``Icon`` component. /// /// This value has a priority over a `textSize`. var iconSize: CGFloat? { @@ -17,36 +17,36 @@ public extension EnvironmentValues { public extension View { - /// Set the size for any Orbit `Icon` component within the view hierarchy. + /// Set the size for any Orbit ``Icon`` component within the view hierarchy. /// /// The `iconSize` value has a priority over the `textSize` environment value that can be used to size Icon to match the text size. /// /// - Parameters: - /// - size: A size that will be used in Orbit `Icon` components. + /// - size: A size that will be used in Orbit ``Icon`` components. /// Pass `nil` to ignore environment icon size and to allow the system or the container to provide its own size. /// If a container-specific override doesn’t exist, the `textSize` or default `.normal` size will be used. func iconSize(_ size: Icon.Size?) -> some View { environment(\.iconSize, size?.value) } - /// Set the size for any Orbit `Icon` component within the view hierarchy. + /// Set the size for any Orbit ``Icon`` component within the view hierarchy. /// /// The `iconSize` value has a priority over the `textSize` environment value that can be used to size Icon to match the text size. /// /// - Parameters: - /// - size: A size that will be used in Orbit `Icon` components. + /// - size: A size that will be used in Orbit ``Icon`` components. /// Pass `nil` to ignore environment icon size and to allow the system or the container to provide its own size. /// If a container-specific override doesn’t exist, the `textSize` or default `.normal` size will be used. func iconSize(custom size: CGFloat?) -> some View { environment(\.iconSize, size) } - /// Set the size, matching line height of a provided text size, for any Orbit `Icon` component within the view hierarchy. + /// Set the size, matching line height of a provided text size, for any Orbit ``Icon`` component within the view hierarchy. /// /// The `iconSize` value has a priority over the `textSize` environment value that can be used to size Icon to match the text size. /// /// - Parameters: - /// - size: A size that will be used in Orbit `Icon` components. + /// - size: A size that will be used in Orbit ``Icon`` components. /// Pass `nil` to ignore environment icon size and to allow the system or the container to provide its own size. /// If a container-specific override doesn’t exist, the `textSize` or default `.normal` size will be used. func iconSize(textSize: Text.Size?) -> some View { diff --git a/Sources/Orbit/Support/Environment Keys/IdealSizeKey.swift b/Sources/Orbit/Support/Environment Keys/IdealSizeKey.swift index eee4e2f81d1..8d7dbedf316 100644 --- a/Sources/Orbit/Support/Environment Keys/IdealSizeKey.swift +++ b/Sources/Orbit/Support/Environment Keys/IdealSizeKey.swift @@ -1,5 +1,6 @@ import SwiftUI +/// The expanding behaviour of compatible Orbit components in a view relative to their ideal size for each axis. public struct IdealSizeValue { public var horizontal: Bool? public var vertical: Bool? @@ -11,6 +12,7 @@ struct IdealSizeKey: EnvironmentKey { public extension EnvironmentValues { + /// The expanding behaviour of compatible Orbit components in a view relative to their ideal size. var idealSize: IdealSizeValue { get { self[IdealSizeKey.self] } set { self[IdealSizeKey.self] = newValue } @@ -19,18 +21,21 @@ public extension EnvironmentValues { public extension View { - /// Sets expanding behaviour of view relative to its ideal size in specified direction. - /// For specified axis, a value of `true` will prevent view from expending beyond its ideal size, - /// while a value of `false` will force view to expand to infinity. + /// Sets the expanding behaviour of compatible Orbit components in a view relative to their ideal size in specified direction. + /// + /// For specified axis, a value of `true` will prevent component from expending beyond its ideal size, + /// while a value of `false` will force component to expand to infinity. A value of `nil` keeps the default behaviour. /// - /// To fix the view exactly to the ideal size, use `fixedSize` modifier instead. + /// - Note: To fix the view exactly to the ideal size, use `fixedSize` modifier instead. func idealSize(horizontal: Bool? = nil, vertical: Bool? = nil) -> some View { environment(\.idealSize, .init(horizontal: horizontal, vertical: vertical)) } - /// Prevents view from additionally expanding beyond its ideal size in both axis. + /// Prevents compatible Orbit components in a view from additionally expanding beyond its ideal size in both axis. /// - /// To fix the view exactly to the ideal size, use `fixedSize` modifier instead. + /// To control separate axis and behaviour, use ``idealSize(horizontal:vertical:)`` modifier. + /// + /// - Note: To fix the view exactly to the ideal size, use `fixedSize` modifier instead. func idealSize() -> some View { idealSize(horizontal: true, vertical: true) } diff --git a/Sources/Orbit/Support/Environment Keys/IsExpandedKey.swift b/Sources/Orbit/Support/Environment Keys/IsExpandedKey.swift index ee73b978445..88c6cd14897 100644 --- a/Sources/Orbit/Support/Environment Keys/IsExpandedKey.swift +++ b/Sources/Orbit/Support/Environment Keys/IsExpandedKey.swift @@ -6,7 +6,7 @@ struct IsExpandedKey: EnvironmentKey { public extension EnvironmentValues { - /// Indicates whether the view should be animated as a result of being expanded or collapsed. + /// Indicates whether the Orbit view should be animated as a result of being expanded or collapsed. /// /// Required for proper animations when view is added or removed using transition. var isExpanded: Bool { diff --git a/Sources/Orbit/Support/Environment Keys/IsFadeInEnabledKey.swift b/Sources/Orbit/Support/Environment Keys/IsFadeInEnabledKey.swift index 50e4aaf4256..715604e0981 100644 --- a/Sources/Orbit/Support/Environment Keys/IsFadeInEnabledKey.swift +++ b/Sources/Orbit/Support/Environment Keys/IsFadeInEnabledKey.swift @@ -6,7 +6,7 @@ struct IsFadeInEnabledKey: EnvironmentKey { public extension EnvironmentValues { - /// Indicates whether a fade-in animation is enabled. + /// Indicates whether a fade-in animation is enabled in Orbit views. var isFadeInEnabled: Bool { get { self[IsFadeInEnabledKey.self] } set { self[IsFadeInEnabledKey.self] = newValue } diff --git a/Sources/Orbit/Support/Environment Keys/IsTileSeparatorVisibleKey.swift b/Sources/Orbit/Support/Environment Keys/IsTileSeparatorVisibleKey.swift index 8c3bc019086..0aa88f8d1b3 100644 --- a/Sources/Orbit/Support/Environment Keys/IsTileSeparatorVisibleKey.swift +++ b/Sources/Orbit/Support/Environment Keys/IsTileSeparatorVisibleKey.swift @@ -6,7 +6,7 @@ struct IsTileSeparatorVisibleKey: EnvironmentKey { public extension EnvironmentValues { - /// Indicates whether a ``Tile``'s separator should be visible. + /// Indicates whether an Orbit ``Tile``'s separator should be visible. var isTileSeparatorVisible: Bool { get { self[IsTileSeparatorVisibleKey.self] } set { self[IsTileSeparatorVisibleKey.self] = newValue } diff --git a/Sources/Orbit/Support/Environment Keys/ScreenLayoutPaddingKey.swift b/Sources/Orbit/Support/Environment Keys/ScreenLayoutPaddingKey.swift index 324625344b9..f59d4ee41dd 100644 --- a/Sources/Orbit/Support/Environment Keys/ScreenLayoutPaddingKey.swift +++ b/Sources/Orbit/Support/Environment Keys/ScreenLayoutPaddingKey.swift @@ -6,7 +6,7 @@ struct ScreenLayoutPaddingKey: EnvironmentKey { public extension EnvironmentValues { - /// Indicates whether the content is inside the `screenLayout` context + /// Indicates whether the content is inside the Orbit `screenLayout` context /// and specifies its padding in case it is. var screenLayoutPadding: ScreenLayoutPadding? { get { self[ScreenLayoutPaddingKey.self] } diff --git a/Sources/Orbit/Support/Environment Keys/StatusKey.swift b/Sources/Orbit/Support/Environment Keys/StatusKey.swift index c6ef758ef6c..be576e41846 100644 --- a/Sources/Orbit/Support/Environment Keys/StatusKey.swift +++ b/Sources/Orbit/Support/Environment Keys/StatusKey.swift @@ -6,7 +6,7 @@ struct StatusKey: EnvironmentKey { public extension EnvironmentValues { - /// An Orbit `Status` stored in a view’s environment. + /// An Orbit ``Status`` stored in a view’s environment. var status: Status? { get { self[StatusKey.self] } set { self[StatusKey.self] = newValue } @@ -15,10 +15,10 @@ public extension EnvironmentValues { public extension View { - /// Set the `Status` for this view. + /// Set the Orbit ``Status`` for this view. /// /// - Parameters: - /// - status: A status that will be used by all components in the view hierarchy. + /// - status: A status that will be used by components in the view hierarchy. func status(_ status: Status?) -> some View { environment(\.status, status) } diff --git a/Sources/Orbit/Support/Environment Keys/TextAccentColorKey.swift b/Sources/Orbit/Support/Environment Keys/TextAccentColorKey.swift index 0b7b37c4a94..202cf1816f8 100644 --- a/Sources/Orbit/Support/Environment Keys/TextAccentColorKey.swift +++ b/Sources/Orbit/Support/Environment Keys/TextAccentColorKey.swift @@ -6,7 +6,7 @@ struct TextAccentColorKey: EnvironmentKey { public extension EnvironmentValues { - /// A text accent color stored in a view’s environment. + /// An Orbit ``Text`` accent color stored in a view’s environment. /// /// - Important: The environment value is different from the native deprecated internal `accentColor` value. var textAccentColor: Color? { @@ -17,7 +17,7 @@ public extension EnvironmentValues { public extension View { - /// Set the text accent color for any Orbit formatted text in this view. + /// Set the accent color for any Orbit formatted text in this view. /// /// - Parameters: /// - color: A color that will be used in `` tags in texts within the view hierarchy. diff --git a/Sources/Orbit/Support/Environment Keys/TextColorKey.swift b/Sources/Orbit/Support/Environment Keys/TextColorKey.swift index 38bcaa6fe14..6ab1afd8405 100644 --- a/Sources/Orbit/Support/Environment Keys/TextColorKey.swift +++ b/Sources/Orbit/Support/Environment Keys/TextColorKey.swift @@ -6,7 +6,7 @@ struct TextColorKey: EnvironmentKey { public extension EnvironmentValues { - /// A text color stored in a view’s environment, used for Orbit text based components, such as `Text`, `Heading` or `Icon`. + /// A text color stored in a view’s environment, used for Orbit text based components, such as ``Text``, ``Heading`` or ``Icon``. /// /// This environment value serves as a replacement for non-public `foregroundColor` environment value. var textColor: Color? { @@ -20,7 +20,7 @@ public extension View { /// Set the text color for any text based Orbit component within the view hierarchy. /// /// - Parameters: - /// - color: A color that will be used in text based Orbit components such as `Text`, `Heading` or `Icon`. + /// - color: A color that will be used in text based Orbit components such as ``Text``, ``Heading`` or ``Icon``. /// Pass `nil` to ignore environment text color and to allow the system or the container to provide its own color. If a container-specific override doesn’t exist, the `inkDark` color will be used. func textColor(_ color: Color?) -> some View { environment(\.textColor, color) diff --git a/Sources/Orbit/Support/Environment Keys/TextIsCopyableKey.swift b/Sources/Orbit/Support/Environment Keys/TextIsCopyableKey.swift index 2b27078af95..141d2e7ee46 100644 --- a/Sources/Orbit/Support/Environment Keys/TextIsCopyableKey.swift +++ b/Sources/Orbit/Support/Environment Keys/TextIsCopyableKey.swift @@ -6,7 +6,7 @@ struct TextIsCopyableKey: EnvironmentKey { public extension EnvironmentValues { - /// A text copyable status stored in a view’s environment, used for Orbit text components, such as `Text` or `Heading`. + /// A text copyable status stored in a view’s environment, used for Orbit text components, such as ``Text`` or ``Heading``. var textIsCopyable: Bool { get { self[TextIsCopyableKey.self] } set { self[TextIsCopyableKey.self] = newValue } @@ -15,11 +15,11 @@ public extension EnvironmentValues { public extension View { - /// Set the copyable status for any text based Orbit component within the view hierarchy. + /// Set the copyable status for any Orbit text based component within the view hierarchy. /// /// - Parameters: /// - enabled: A value that will be used to decide whether text based Orbit components - /// such as `Text` or `Heading` can be copied using long tap gesture. + /// such as ``Text`` or ``Heading`` can be copied using long tap gesture. func textIsCopyable(_ enabled: Bool = true) -> some View { environment(\.textIsCopyable, enabled) } diff --git a/Sources/Orbit/Support/Environment Keys/TextLineHeightKey.swift b/Sources/Orbit/Support/Environment Keys/TextLineHeightKey.swift index 796e2ce8686..5d6faa26c23 100644 --- a/Sources/Orbit/Support/Environment Keys/TextLineHeightKey.swift +++ b/Sources/Orbit/Support/Environment Keys/TextLineHeightKey.swift @@ -6,7 +6,7 @@ struct TextLineHeightKey: EnvironmentKey { public extension EnvironmentValues { - /// A text line height stored in a view’s environment, used for Orbit text based components, such as `Text` or `Heading`. + /// A text line height stored in a view’s environment, used for Orbit text based components, such as ``Text`` or ``Heading``. /// /// This environment value serves as a replacement for native line height. var textLineHeight: CGFloat? { @@ -20,7 +20,7 @@ public extension View { /// Set the custom line height for any text based Orbit component within the view hierarchy. /// /// - Parameters: - /// - height: An overall height that will be used in text based Orbit components such as `Text` or `Heading`. + /// - height: An overall height that will be used in text based Orbit components such as ``Text`` or ``Heading``. /// Pass `nil` to ignore environment text size and to allow the system or the container to provide its own height. /// If a container-specific override doesn’t exist, the line height will be calculated from text size. func textLineHeight(_ height: CGFloat?) -> some View { diff --git a/Sources/Orbit/Support/Environment Keys/TextLinkActionKey.swift b/Sources/Orbit/Support/Environment Keys/TextLinkActionKey.swift index c9136845aa2..7bc6c27f9a2 100644 --- a/Sources/Orbit/Support/Environment Keys/TextLinkActionKey.swift +++ b/Sources/Orbit/Support/Environment Keys/TextLinkActionKey.swift @@ -6,7 +6,7 @@ struct TextLinkActionKey: EnvironmentKey { public extension EnvironmentValues { - /// A `TextLink` action stored in a view’s environment. + /// Orbit `TextLink` ``TextLink/Action`` stored in a view’s environment. var textLinkAction: TextLink.Action? { get { self[TextLinkActionKey.self] } set { self[TextLinkActionKey.self] = newValue } @@ -15,10 +15,10 @@ public extension EnvironmentValues { public extension View { - /// Set the `TextLink` action for this view. + /// Set the `TextLink` ``TextLink/Action`` for text links found in this view. /// /// - Parameters: - /// - action: A handler that is executed when the user taps on any `TextLink` inside the view hierarchy. + /// - action: A handler that is executed when the user taps on any ``TextLink`` inside the view hierarchy. func textLinkAction(_ action: @escaping TextLink.Action) -> some View { environment(\.textLinkAction, action) } diff --git a/Sources/Orbit/Support/Environment Keys/TextLinkColorKey.swift b/Sources/Orbit/Support/Environment Keys/TextLinkColorKey.swift index 5c8030ea34f..6037974d066 100644 --- a/Sources/Orbit/Support/Environment Keys/TextLinkColorKey.swift +++ b/Sources/Orbit/Support/Environment Keys/TextLinkColorKey.swift @@ -6,7 +6,7 @@ struct TextLinkColorKey: EnvironmentKey { public extension EnvironmentValues { - /// A `TextLink` color stored in a view’s environment. + /// Orbit `TextLink` ``TextLink/Color`` color stored in a view’s environment. var textLinkColor: TextLink.Color? { get { self[TextLinkColorKey.self] } set { self[TextLinkColorKey.self] = newValue } @@ -15,10 +15,10 @@ public extension EnvironmentValues { public extension View { - /// Override the default `TextLink` color for this view. + /// Override the default Orbit `TextLink` ``TextLink/Color` for this view. /// /// - Parameters: - /// - color: A color that will be used by all `TextLink`s inside the view hierarchy. + /// - color: A color that will be used by all ``TextLink``s inside the view hierarchy. func textLinkColor(_ color: TextLink.Color?) -> some View { environment(\.textLinkColor, color) } diff --git a/Sources/Orbit/Support/Environment Keys/TextSizeKey.swift b/Sources/Orbit/Support/Environment Keys/TextSizeKey.swift index 39de070708f..69ad3ccd35e 100644 --- a/Sources/Orbit/Support/Environment Keys/TextSizeKey.swift +++ b/Sources/Orbit/Support/Environment Keys/TextSizeKey.swift @@ -6,7 +6,7 @@ struct TextSizeKey: EnvironmentKey { public extension EnvironmentValues { - /// A text size stored in a view’s environment, used for Orbit text based components, such as `Text`, `Heading` or `Icon`. + /// A text size stored in a view’s environment, used for Orbit text based components, such as ``Text``, ``Heading`` or ``Icon``. /// /// This environment value serves as a replacement for non-public `font` environment value. var textSize: CGFloat? { @@ -20,7 +20,7 @@ public extension View { /// Set the text size for any text based Orbit component within the view hierarchy. /// /// - Parameters: - /// - size: A size that will be used in text based Orbit components such as `Text` or `Icon`. + /// - size: A size that will be used in text based Orbit components such as ``Text`` or ``Icon``. /// Pass `nil` to ignore environment text size and to allow the system or the container to provide its own size. /// If a container-specific override doesn’t exist, the `.normal` size will be used. func textSize(_ size: Text.Size?) -> some View { @@ -31,7 +31,7 @@ public extension View { /// Set the custom text size for any text based Orbit component within the view hierarchy. /// /// - Parameters: - /// - size: A size that will be used in text based Orbit components such as `Text` or `Icon`. + /// - size: A size that will be used in text based Orbit components such as ``Text`` or ``Icon``. /// Pass `nil` to ignore environment text size and to allow the system or the container to provide its own size. /// If a container-specific override doesn’t exist, the `.normal` size will be used. func textSize(custom size: CGFloat?) -> some View { diff --git a/Sources/Orbit/Support/Forms/FieldLabel.swift b/Sources/Orbit/Support/Forms/FieldLabel.swift index 69b7f16bf9f..edfb5e89cd8 100644 --- a/Sources/Orbit/Support/Forms/FieldLabel.swift +++ b/Sources/Orbit/Support/Forms/FieldLabel.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Orbit label positioned above a form field. +/// Orbit support component that displays label positioned above a form field. public struct FieldLabel: View { private let label: String @@ -10,7 +10,7 @@ public struct FieldLabel: View { .fontWeight(.medium) } - /// Create Orbit form field label. + /// Creates Orbit ``FieldLabel``. public init(_ label: String) { self.label = label } diff --git a/Sources/Orbit/Support/Forms/FieldMessage.swift b/Sources/Orbit/Support/Forms/FieldMessage.swift index fafd8571ae1..209c2d26447 100644 --- a/Sources/Orbit/Support/Forms/FieldMessage.swift +++ b/Sources/Orbit/Support/Forms/FieldMessage.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Orbit message below form fields. +/// Orbit support component that displays message below form fields. public struct FieldMessage: View { @Environment(\.sizeCategory) private var sizeCategory @@ -28,6 +28,7 @@ public struct FieldMessage: View { } } + /// Creates Orbit ``FieldMessage``. public init(_ message: Message?, spacing: CGFloat = .xxSmall) { self.message = message self.spacing = spacing diff --git a/Sources/Orbit/Support/Forms/FieldWrapper.swift b/Sources/Orbit/Support/Forms/FieldWrapper.swift index d21e49d8a94..e9bfedfd603 100644 --- a/Sources/Orbit/Support/Forms/FieldWrapper.swift +++ b/Sources/Orbit/Support/Forms/FieldWrapper.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Orbit wrapper around form fields. Provides optional label and message. +/// Orbit support component that orovides label and message around input field. public struct FieldWrapper: View { @Binding private var messageHeight: CGFloat @@ -35,9 +35,9 @@ public struct FieldWrapper: View { // MARK: - Inits public extension FieldWrapper { - /// Creates Orbit wrapper around form field content with a custom label and an additional message content. + /// Creates Orbit ``FieldWrapper`` around form field content with a custom label and an additional message content. /// - /// `FieldLabel` is a default component for constructing custom label. + /// ``FieldLabel`` is a default component for constructing custom label. init( message: Message? = nil, messageHeight: Binding = .constant(0), @@ -55,7 +55,7 @@ public extension FieldWrapper { public extension FieldWrapper where Label == FieldLabel { - /// Creates Orbit wrapper around form field content with an additional message content. + /// Creates Orbit ``FieldWrapper`` around form field content with an additional message content. init( _ label: String, message: Message? = nil, diff --git a/Sources/Orbit/Support/Forms/InputContent.swift b/Sources/Orbit/Support/Forms/InputContent.swift index 2fc974bc39f..4cbed3c89ff 100644 --- a/Sources/Orbit/Support/Forms/InputContent.swift +++ b/Sources/Orbit/Support/Forms/InputContent.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Content for Orbit inputs that share common layout with a prefix and suffix. +/// Orbit support component representing input components that share common layout with a prefix and suffix. public struct InputContent: View { @Environment(\.iconColor) private var iconColor @@ -124,7 +124,7 @@ public struct InputContent: View { } } - /// Create content for Orbit inputs that share common layout with a prefix and suffix. + /// Create Orbit ``InputContent``. public init( state: InputState = .default, label: String = "", diff --git a/Sources/Orbit/Support/Forms/InputState.swift b/Sources/Orbit/Support/Forms/InputState.swift index 0406a8d7314..d05150e86ad 100644 --- a/Sources/Orbit/Support/Forms/InputState.swift +++ b/Sources/Orbit/Support/Forms/InputState.swift @@ -1,6 +1,6 @@ import SwiftUI -/// State of Orbit input. +/// State of Orbit input components. public enum InputState { case `default` diff --git a/Sources/Orbit/Support/Forms/KeyValueField.swift b/Sources/Orbit/Support/Forms/KeyValueField.swift index 1ae0a562a8a..7b841211e24 100644 --- a/Sources/Orbit/Support/Forms/KeyValueField.swift +++ b/Sources/Orbit/Support/Forms/KeyValueField.swift @@ -1,15 +1,27 @@ import SwiftUI -/// A `KeyValue` container with generic content. +/// Orbit support component that displays a pair of label and a content. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/keyvalue/) +/// A ``KeyValueField`` consists of a label and a content. +/// +/// ```swift +/// KeyValueField("First Name") { +/// Text("Pavel") +/// } +/// ``` +/// +/// ### Layout +/// +/// The text alignment can be modified by ``multilineTextAlignment(_:)``. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/keyvalue/) public struct KeyValueField: View { @Environment(\.multilineTextAlignment) private var multilineTextAlignment - let key: String - let size: KeyValue.Size - @ViewBuilder let content: Content + private let key: String + private let size: KeyValue.Size + @ViewBuilder private let content: Content public var body: some View { VStack(alignment: .init(multilineTextAlignment), spacing: 0) { @@ -25,10 +37,10 @@ public struct KeyValueField: View { } // MARK: - Inits -extension KeyValueField { +public extension KeyValueField { - /// Creates Orbit KeyValue component. - public init( + /// Creates Orbit ``KeyValueField`` support component. + init( _ key: String = "", size: KeyValue.Size = .normal, @ViewBuilder content: () -> Content @@ -63,6 +75,7 @@ struct KeyValueFieldPreviews: PreviewProvider { KeyValueField("Multiline and very long key") { Text("Multiline and very long value") } + .multilineTextAlignment(.trailing) .frame(width: 100) .multilineTextAlignment(.trailing) } diff --git a/Sources/Orbit/Support/Forms/Message.swift b/Sources/Orbit/Support/Forms/Message.swift index 3fa86c45afc..1bc7022a3a3 100644 --- a/Sources/Orbit/Support/Forms/Message.swift +++ b/Sources/Orbit/Support/Forms/Message.swift @@ -1,5 +1,6 @@ import SwiftUI +/// Orbit informational message used in forms. public enum Message: Equatable, Hashable, CustomStringConvertible { case normal(String, icon: Icon.Symbol? = nil) diff --git a/Sources/Orbit/Support/HapticsProvider.swift b/Sources/Orbit/Support/HapticsProvider.swift index 3c6e5dede38..af7fe9253c2 100644 --- a/Sources/Orbit/Support/HapticsProvider.swift +++ b/Sources/Orbit/Support/HapticsProvider.swift @@ -1,5 +1,9 @@ import UIKit +import SwiftUI +/// Orbit haptics feedback provider. +/// +/// Can be disabled by ``SwiftUI/View/hapticsDisabled(_:)``. public enum HapticsProvider { public enum HapticFeedbackType { diff --git a/Sources/Orbit/Support/Layout/ContentHeightReader.swift b/Sources/Orbit/Support/Layout/ContentHeightReader.swift index d6dc5305974..ff1ff6313cf 100644 --- a/Sources/Orbit/Support/Layout/ContentHeightReader.swift +++ b/Sources/Orbit/Support/Layout/ContentHeightReader.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Invisible wrapper that communicates its current content height. +/// Invisible Orbit wrapper that communicates its current content height. public struct ContentHeightReader: View { @Binding var height: CGFloat diff --git a/Sources/Orbit/Support/Layout/HorizontalScrollReader.swift b/Sources/Orbit/Support/Layout/HorizontalScrollReader.swift index 2d3f2022253..8ae36a1b986 100644 --- a/Sources/Orbit/Support/Layout/HorizontalScrollReader.swift +++ b/Sources/Orbit/Support/Layout/HorizontalScrollReader.swift @@ -1,5 +1,6 @@ import SwiftUI +/// A proxy for Orbit ``HorizontalScrollReader``. @available(iOS 14, *) public class HorizontalScrollViewProxy: ObservableObject { @@ -10,14 +11,13 @@ public class HorizontalScrollViewProxy: ObservableObject { } } -/// A view that provides programmatic scrolling of `HorizontalScroll` component, -/// by working with a proxy to scroll to child views marked by `identifier()`. +/// Orbit component that provides programmatic scrolling of ``HorizontalScroll`` component, +/// by working with a ``HorizontalScrollViewProxy`` to scroll to child views marked by Orbit `identifier()` modifier. @available(iOS 14, *) public struct HorizontalScrollReader: View { - @ViewBuilder let content: (HorizontalScrollViewProxy) -> Content - @StateObject private var proxy = HorizontalScrollViewProxy() + @ViewBuilder private let content: (HorizontalScrollViewProxy) -> Content public var body: some View { content(proxy) @@ -28,6 +28,7 @@ public struct HorizontalScrollReader: View { .environment(\.scrollTarget, proxy.scrollTarget) } + /// Creates Orbit ``HorizontalScrollReader`` component. public init(@ViewBuilder content: @escaping (HorizontalScrollViewProxy) -> Content) { self.content = content } diff --git a/Sources/Orbit/Support/Layout/IgnoreScreenLayoutHorizontalPaddingModifier.swift b/Sources/Orbit/Support/Layout/IgnoreScreenLayoutHorizontalPaddingModifier.swift index 4033a3a9893..f7a8ebf76eb 100644 --- a/Sources/Orbit/Support/Layout/IgnoreScreenLayoutHorizontalPaddingModifier.swift +++ b/Sources/Orbit/Support/Layout/IgnoreScreenLayoutHorizontalPaddingModifier.swift @@ -22,9 +22,9 @@ struct IgnoreScreenLayoutHorizontalPaddingModifier: ViewModifier { public extension View { - /// Reverts any horizontal padding provided by `screenLayout` context. Can be optionally limited to a specific horizontal size class. + /// Reverts any horizontal padding provided by Orbit `screenLayout` context. Can be optionally limited to a specific horizontal size class. /// - /// A typical usage is to mimic the edge-to-edge appearance of the `Card` component. + /// A typical usage is to mimic the edge-to-edge appearance of the ``Card`` component. func ignoreScreenLayoutHorizontalPadding(limitToSizeClass sizeClass: UserInterfaceSizeClass? = nil) -> some View { modifier(IgnoreScreenLayoutHorizontalPaddingModifier(limitToSizeClass: sizeClass)) } diff --git a/Sources/Orbit/Support/Layout/Layout.swift b/Sources/Orbit/Support/Layout/Layout.swift index c8799b982f0..d8c343d950d 100644 --- a/Sources/Orbit/Support/Layout/Layout.swift +++ b/Sources/Orbit/Support/Layout/Layout.swift @@ -2,7 +2,7 @@ import SwiftUI public enum Layout { - /// Maximum readable width used for layout in regular width environment. + /// Maximum readable width used for Orbit layout in regular width environment. public static var readableMaxWidth: CGFloat = 672 } diff --git a/Sources/Orbit/Support/Layout/LazyVStack.swift b/Sources/Orbit/Support/Layout/LazyVStack.swift index 93b3674303f..d417b705930 100644 --- a/Sources/Orbit/Support/Layout/LazyVStack.swift +++ b/Sources/Orbit/Support/Layout/LazyVStack.swift @@ -1,5 +1,6 @@ import SwiftUI +@available(iOS, deprecated: 14.0, message: "Use native variant") struct LazyVStack: View { var alignment: HorizontalAlignment = .center diff --git a/Sources/Orbit/Support/Layout/PotentiallyEmptyView.swift b/Sources/Orbit/Support/Layout/PotentiallyEmptyView.swift index e8f07aa7367..c744142dfd6 100644 --- a/Sources/Orbit/Support/Layout/PotentiallyEmptyView.swift +++ b/Sources/Orbit/Support/Layout/PotentiallyEmptyView.swift @@ -1,5 +1,5 @@ -/// A type that represents views that can optionally result in `EmptyView`. +/// A type that represents Orbit components that can optionally result in `EmptyView`. protocol PotentiallyEmptyView { var isEmpty: Bool { get } } diff --git a/Sources/Orbit/Support/Layout/ScreenLayoutModifier.swift b/Sources/Orbit/Support/Layout/ScreenLayoutModifier.swift index f73092e4638..0c3ab1ea33a 100644 --- a/Sources/Orbit/Support/Layout/ScreenLayoutModifier.swift +++ b/Sources/Orbit/Support/Layout/ScreenLayoutModifier.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Padding to apply in a `screenLayout` context. +/// Padding to apply in Orbit `screenLayout` context. public enum ScreenLayoutPadding { /// Padding of `medium` size in compact horizontal size and `xxLarge` size in regular horizontal size. case `default` @@ -65,7 +65,7 @@ struct ScreenLayoutModifier: ViewModifier { public extension View { - /// Adds unified screen layout for both `regular` and `compact` width environment. + /// Adds unified Orbit layout to a screen for both `regular` and `compact` width environment. /// /// Screen layout adds maximum width and padding behaviour for provided content, horizontally expanding to `infinity`. /// diff --git a/Sources/Orbit/Support/Resources/AssetNameProviding.swift b/Sources/Orbit/Support/Resources/AssetNameProviding.swift index ef501b7f085..cd4820b417f 100644 --- a/Sources/Orbit/Support/Resources/AssetNameProviding.swift +++ b/Sources/Orbit/Support/Resources/AssetNameProviding.swift @@ -1,4 +1,5 @@ +/// Type that provides asset name for Orbit components. public protocol AssetNameProviding: RawRepresentable { var assetName: String { get } diff --git a/Sources/Orbit/Support/Resources/Bundle.swift b/Sources/Orbit/Support/Resources/Bundle.swift index 52dea0af50c..a55c156f9cc 100644 --- a/Sources/Orbit/Support/Resources/Bundle.swift +++ b/Sources/Orbit/Support/Resources/Bundle.swift @@ -1,5 +1,7 @@ import Foundation public extension Bundle { + + /// A bundle that defines Orbit resources. static let orbit = Bundle.module } diff --git a/Sources/Orbit/Support/Status.swift b/Sources/Orbit/Support/Status.swift index 05cd802b214..dc7d8f347fd 100644 --- a/Sources/Orbit/Support/Status.swift +++ b/Sources/Orbit/Support/Status.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Non-default status of a component. +/// Non-default status of Orbit components. public enum Status: Equatable { case info case success diff --git a/Sources/Orbit/Support/Text/TextRepresentable.swift b/Sources/Orbit/Support/Text/TextRepresentable.swift index 346cc76e074..883d025c597 100644 --- a/Sources/Orbit/Support/Text/TextRepresentable.swift +++ b/Sources/Orbit/Support/Text/TextRepresentable.swift @@ -1,10 +1,11 @@ import SwiftUI -/// A type that can be represented as `SwiftUI.Text` +/// A type that can be represented as `SwiftUI.Text` and can serve as Orbit concatenable component. /// -/// Use the `+` operator to concatenate two `TextRepresentable` elements. +/// Use the `+` operator to concatenate ``TextRepresentable`` elements. public protocol TextRepresentable { + /// Extracts native `SwiftUI.Text` from type that represents textual content. func swiftUIText(textRepresentableEnvironment: TextRepresentableEnvironment) -> SwiftUI.Text? } diff --git a/Sources/Orbit/Support/TextFields/InsetableTextField.swift b/Sources/Orbit/Support/TextFields/InsetableTextField.swift index b080523f8bf..b878b747da3 100644 --- a/Sources/Orbit/Support/TextFields/InsetableTextField.swift +++ b/Sources/Orbit/Support/TextFields/InsetableTextField.swift @@ -1,6 +1,6 @@ import UIKit -/// Orbit UITextField wrapper with a larger touch area. +/// Orbit `UITextField` wrapper with a larger touch area. public class InsetableTextField: UITextField { // Using .small vertical padding would cause resize issue in secure mode diff --git a/Sources/Orbit/Support/TextFields/InsetableTextView.swift b/Sources/Orbit/Support/TextFields/InsetableTextView.swift index d388e685872..a30f93d49fc 100644 --- a/Sources/Orbit/Support/TextFields/InsetableTextView.swift +++ b/Sources/Orbit/Support/TextFields/InsetableTextView.swift @@ -1,6 +1,6 @@ import UIKit -/// Orbit UITextView wrapper with a larger touch area and a prompt. +/// Orbit `UITextView` wrapper with a larger touch area and a prompt. public class InsetableTextView: UITextView { public var insets: UIEdgeInsets = .zero diff --git a/Sources/Orbit/Support/TextFields/TextField.swift b/Sources/Orbit/Support/TextFields/TextField.swift index a52c70049a6..bb6eda55f54 100644 --- a/Sources/Orbit/Support/TextFields/TextField.swift +++ b/Sources/Orbit/Support/TextFields/TextField.swift @@ -1,7 +1,7 @@ import SwiftUI import UIKit -/// Orbit control that displays an editable text interface, a replacement for native `TextField` component. +/// Orbit control that displays an editable text interface, a replacement for native `SwiftUI.TextField` component. /// /// The component uses UIKit `UITextField` implementation to support these feature for older iOS versions: /// - focus changes diff --git a/Sources/Orbit/Support/TextFields/TextFieldCoordinator.swift b/Sources/Orbit/Support/TextFields/TextFieldCoordinator.swift index 064ccd68a1c..5dcdd7d5222 100644 --- a/Sources/Orbit/Support/TextFields/TextFieldCoordinator.swift +++ b/Sources/Orbit/Support/TextFields/TextFieldCoordinator.swift @@ -1,6 +1,6 @@ import SwiftUI -/// Coordinator that manages Orbit text input components `InputField` and `Textarea`. +/// Coordinator that manages Orbit text input components ``InputField`` and ``Textarea``. public final class TextFieldCoordinator: NSObject, ObservableObject { static var textFieldToBecomeResponder: UITextInput? diff --git a/Sources/Orbit/Support/TextFields/TextView.swift b/Sources/Orbit/Support/TextFields/TextView.swift index bbfb3893a1d..021d63eb667 100644 --- a/Sources/Orbit/Support/TextFields/TextView.swift +++ b/Sources/Orbit/Support/TextFields/TextView.swift @@ -1,7 +1,7 @@ import SwiftUI import UIKit -/// Orbit control that displays a multiline editable text interface, a replacement for the native multiline `TextField` component. +/// Orbit control that displays a multiline editable text interface, a replacement for the native multiline `SwiftUI.TextField` component. /// /// The component uses UIKit `UITextView` implementation to support these feature for older iOS versions: /// - focus changes diff --git a/Sources/Orbit/Support/TextLinks/TagAttributedStringBuilder.swift b/Sources/Orbit/Support/TextLinks/TagAttributedStringBuilder.swift index e36f5087066..014d974333d 100644 --- a/Sources/Orbit/Support/TextLinks/TagAttributedStringBuilder.swift +++ b/Sources/Orbit/Support/TextLinks/TagAttributedStringBuilder.swift @@ -1,8 +1,7 @@ import UIKit import SwiftUI -// Duplicate of TagAttributedStringBuilder in SharedUI with slight alterations. -@available(iOS, deprecated: 15.0, message: "Will be replaced with a native markdown-enabled Text component") +@available(iOS, deprecated: 15.0, message: "Use native markdown-enabled Text component") final class TagAttributedStringBuilder { enum Tag: Equatable, CaseIterable { @@ -172,7 +171,7 @@ struct TagAttributedStringBuilderTagFinder { } } -public extension String { +extension String { var containsHtmlFormatting: Bool { TagAttributedStringBuilder.all.tagFinder.hasMatches(for: self) diff --git a/Sources/Orbit/Support/TextLinks/Text+AttributedString.swift b/Sources/Orbit/Support/TextLinks/Text+AttributedString.swift index d3fff89aa71..f47e8ea8b44 100644 --- a/Sources/Orbit/Support/TextLinks/Text+AttributedString.swift +++ b/Sources/Orbit/Support/TextLinks/Text+AttributedString.swift @@ -2,6 +2,7 @@ import SwiftUI public extension SwiftUI.Text { + /// Multiplier for Orbit font kerning. static var kerningMultiplier: CGFloat { if #available(iOS 16.0, *) { return 1.0 @@ -10,8 +11,8 @@ public extension SwiftUI.Text { } } - /// Creates a native SwiftUI Text from attributed string. - @available(iOS, deprecated: 15.0, message: "Will be replaced with a native init") + /// Creates a native `SwiftUI.Text` from attributed string. + @available(iOS, deprecated: 15.0, message: "Use native initializer", renamed: "init(_:)") init(_ string: NSAttributedString) { self.init("") diff --git a/Sources/Orbit/Support/TextLinks/TextLinkView.swift b/Sources/Orbit/Support/TextLinks/TextLinkView.swift index c08c174c6b8..3ab25ad09c4 100644 --- a/Sources/Orbit/Support/TextLinks/TextLinkView.swift +++ b/Sources/Orbit/Support/TextLinks/TextLinkView.swift @@ -1,7 +1,7 @@ import UIKit import SwiftUI -/// A view representing `TextLink` layer. +/// A view that represents the Orbit ``TextLink`` layer. public final class TextLinkView: UITextView, UITextViewDelegate { let action: TextLink.Action diff --git a/Sources/Orbit/Support/Toast/ToastQueue.swift b/Sources/Orbit/Support/Toast/ToastQueue.swift index 016fa564255..688bbef4dfd 100644 --- a/Sources/Orbit/Support/Toast/ToastQueue.swift +++ b/Sources/Orbit/Support/Toast/ToastQueue.swift @@ -1,9 +1,9 @@ import Combine import SwiftUI -/// Serial queue for Orbit Toast component. +/// Serial queue for Orbit ``Toast`` component. /// -/// Allows adding new Toasts and dismissing or pausing the currently displayed Toast. +/// Allows adding new messages and dismissing or pausing the currently displayed message in a ``Toast``. public final class ToastQueue: ObservableObject { public static let toastsBufferSize = 5 @@ -18,7 +18,7 @@ public final class ToastQueue: ObservableObject { case dismiss } - /// View model for Orbit Toast component. + /// View model for Orbit ``Toast`` component. public struct Toast: Identifiable { public let id: UUID let description: String @@ -48,6 +48,7 @@ public final class ToastQueue: ObservableObject { private var toastsSubject = PassthroughSubject() private var currentToastActionSubject: CurrentValueSubject? + /// Creates Orbit ``ToastQueue``. public init() { cancellable = toastsSubject .buffer(size: Self.toastsBufferSize, prefetch: .keepFull, whenFull: .dropOldest) diff --git a/Sources/OrbitIllustrations/Components/Illustration.swift b/Sources/OrbitIllustrations/Components/Illustration.swift index c44d801f30e..d071e069f53 100644 --- a/Sources/OrbitIllustrations/Components/Illustration.swift +++ b/Sources/OrbitIllustrations/Components/Illustration.swift @@ -1,10 +1,21 @@ import SwiftUI import Orbit -/// An illustration matching Orbit name. +/// Orbit component that displays an illustration. /// -/// - Note: [Orbit definition](https://orbit.kiwi/components/illustration/) -/// - Important: The component expands horizontally to infinity in case of `frame` layout, unless prevented by `idealSize` modifier. +/// An ``Illustration`` is created using Orbit or custom resource. +/// +/// ```swift +/// Illustration(.womanWithPhone) +/// Illustration("my-illustration", layout: .resizeable) +/// .frame(height: 50) +/// ``` +/// +/// ### Layout +/// +/// The component expands horizontally to infinity in case of the default `frame` layout, unless prevented by `idealSize` modifier. A `resizeable` layout makes the illustration size to fit to desired size. +/// +/// - Note: [Orbit.kiwi documentation](https://orbit.kiwi/components/illustration/) public struct Illustration: View { @Environment(\.idealSize) var idealSize diff --git a/Sources/OrbitIllustrations/Extensions/ChoiceTile+Illustration.swift b/Sources/OrbitIllustrations/Extensions/ChoiceTile+Illustration.swift index 5a966d52687..9526f7cfef3 100644 --- a/Sources/OrbitIllustrations/Extensions/ChoiceTile+Illustration.swift +++ b/Sources/OrbitIllustrations/Extensions/ChoiceTile+Illustration.swift @@ -3,7 +3,7 @@ import Orbit public extension ChoiceTile { - /// Creates Orbit ChoiceTile component. + /// Creates Orbit ``ChoiceTile`` component with illustration. /// /// - Parameters: /// - content: The content shown below the header. @@ -43,7 +43,7 @@ public extension ChoiceTile { } } - /// Creates Orbit ChoiceTile component. + /// Creates Orbit ``ChoiceTile`` component with illustration. /// /// - Parameters: /// - content: The content shown below the header. diff --git a/Sources/OrbitIllustrations/Extensions/Dialog+Illustration.swift b/Sources/OrbitIllustrations/Extensions/Dialog+Illustration.swift index c3f358e5222..59bd1d4dffe 100644 --- a/Sources/OrbitIllustrations/Extensions/Dialog+Illustration.swift +++ b/Sources/OrbitIllustrations/Extensions/Dialog+Illustration.swift @@ -3,7 +3,7 @@ import Orbit public extension Dialog { - /// Creates Orbit Dialog component with an illustration. + /// Creates Orbit ``Dialog`` component with an illustration. init( _ title: String = "", description: String = "", diff --git a/Sources/OrbitIllustrations/Extensions/EmptyState+Illustration.swift b/Sources/OrbitIllustrations/Extensions/EmptyState+Illustration.swift index e6f9dcfbf43..6352a6609c4 100644 --- a/Sources/OrbitIllustrations/Extensions/EmptyState+Illustration.swift +++ b/Sources/OrbitIllustrations/Extensions/EmptyState+Illustration.swift @@ -3,7 +3,7 @@ import Orbit public extension EmptyState { - /// Creates Orbit EmptyState component with illustration. + /// Creates Orbit ``EmptyState`` component with illustration. init( _ title: String = "", description: String = "", @@ -16,7 +16,7 @@ public extension EmptyState { } } - /// Creates Orbit EmptyState component with illustration and no action. + /// Creates Orbit ``EmptyState`` component with illustration and no action. init( _ title: String = "", description: String = "",