diff --git a/CHANGELOG.md b/CHANGELOG.md index 132cdf8d7..6440c392a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- [Library] Checkbox component ([#264](https://github.com/Orange-OpenSource/ouds-ios/issues/264)) - [Library] Switch component ([#405](https://github.com/Orange-OpenSource/ouds-ios/issues/405)) - [Library] Link component ([#400](https://github.com/Orange-OpenSource/ouds-ios/issues/400)) diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabel.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabel.swift new file mode 100644 index 000000000..f8b6dbef6 --- /dev/null +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabel.swift @@ -0,0 +1,98 @@ +// +// Software Name: OUDS iOS +// SPDX-FileCopyrightText: Copyright (c) Orange SA +// SPDX-License-Identifier: MIT +// +// This software is distributed under the MIT license, +// the text of which is available at https://opensource.org/license/MIT/ +// or see the "LICENSE" file for more details. +// +// Authors: See CONTRIBUTORS.txt +// Software description: A SwiftUI components library with code examples for Orange Unified Design System +// + +import OUDS +import OUDSTokensSemantic +import SwiftUI + +/// The trailing part of the checkbox component, i.e. all the views without the selector, i.e. texts and images +struct OUDSCheckboxLabel: View { + + // MARK: - Properties + + let internalState: InternalCheckboxState + let label: Label + + @Environment(\.theme) private var theme + @Environment(\.colorScheme) private var colorScheme + + // MARK: - Item + + struct Label { + let label: String + let helperText: String? + let icon: Image? + let onError: Bool + let divider: Bool + } + + // MARK: - Body + + var body: some View { + HStack(spacing: theme.listItem.listItemSpaceColumnGap) { + VStack(alignment: .leading, spacing: 0) { + Text(LocalizedStringKey(label.label)) + .typeLabelDefaultLarge(theme) + .multilineTextAlignment(.leading) + .foregroundStyle(labelColor) + .frame(maxWidth: .infinity, alignment: .leading) + + if let helperText = label.helperText { + Text(LocalizedStringKey(helperText)) + .typeLabelDefaultMedium(theme) + .multilineTextAlignment(.leading) + .foregroundStyle(helperTextColor) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + + if let icon = label.icon { + icon + .resizable() + .renderingMode(.template) + .foregroundStyle(iconColor) + .frame(width: theme.listItem.listItemSizeIcon, height: theme.listItem.listItemSizeIcon) + } + } + } + + // MARK: - Colors + + private var labelColor: Color { + switch internalState { + case .enabled, .pressed, .hover: + (label.onError ? theme.colors.colorContentStatusNegative : theme.colors.colorContentDefault) + .color(for: colorScheme) + case .disabled, .readOnly: + theme.colors.colorContentDisabled.color(for: colorScheme) + } + } + + private var iconColor: Color { + switch internalState { + case .enabled, .pressed, .hover: + theme.colors.colorContentDefault.color(for: colorScheme) + case .disabled, .readOnly: + theme.colors.colorContentDisabled.color(for: colorScheme) + } + } + + private var helperTextColor: Color { + switch internalState { + case .enabled, .pressed, .hover: + theme.colors.colorContentMuted.color(for: colorScheme) + case .disabled, .readOnly: + theme.colors.colorContentDisabled.color(for: colorScheme) + } + } +} diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabeledStyle.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabeledStyle.swift new file mode 100644 index 000000000..8507dbfe7 --- /dev/null +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxLabeledStyle.swift @@ -0,0 +1,91 @@ +// +// Software Name: OUDS iOS +// SPDX-FileCopyrightText: Copyright (c) Orange SA +// SPDX-License-Identifier: MIT +// +// This software is distributed under the MIT license, +// the text of which is available at https://opensource.org/license/MIT/ +// or see the "LICENSE" file for more details. +// +// Authors: See CONTRIBUTORS.txt +// Software description: A SwiftUI components library with code examples for Orange Unified Design System +// + +import OUDS +import OUDSTokensSemantic +import SwiftUI + +// MARK: - Internal Checkbox State + +/// The internal state used by modifiers to handle all states of the button. +enum InternalCheckboxState { + case enabled + case hover + case pressed + case disabled + case readOnly + // .loading not managed yet, for next version + // .focus not managed as not that much customizable + // .skeleton not managed as dedicated view in the end +} + +// MARK: - Checkbox Labeled Style + +struct OUDSCheckboxLabeledStyle: ButtonStyle { + + let isOn: Bool // TODO: #264 - Manage the three states + let label: OUDSCheckboxLabel.Label + let inverse: Bool + + @State private var isHover: Bool = false + @Environment(\.isEnabled) private var isEnabled + @Environment(\.theme) private var theme + @Environment(\.colorScheme) private var colorScheme + + // MARK: - Body + + func makeBody(configuration: Configuration) -> some View { + HStack(alignment: .center, spacing: theme.listItem.listItemSpaceColumnGap) { + OUDSCheckboxSelectorButton(internalState: internalState(isPressed: configuration.isPressed), isOn: isOn) + + OUDSCheckboxLabel(internalState: internalState(isPressed: configuration.isPressed), label: label) + } + .padding(.all, theme.listItem.listItemSpaceInset) + .oudsDivider(show: label.divider) + .background(backgroundColor(state: internalState(isPressed: configuration.isPressed))) + .onHover { isHover in + self.isHover = isHover + } + } + + // MARK: - Helpers + + func backgroundColor(state: InternalCheckboxState) -> Color { + switch state { + case .enabled: + theme.select.selectColorBgEnabled.color(for: colorScheme) + case .hover: + theme.select.selectColorBgHover.color(for: colorScheme) + case .pressed: + theme.select.selectColorBgPressed.color(for: colorScheme) + case .disabled, .readOnly: + theme.select.selectColorBgDisabled.color(for: colorScheme) + } + } + + private func internalState(isPressed: Bool) -> InternalCheckboxState { + if !isEnabled { + return .disabled + } + + if isPressed { + return .pressed + } + + if isHover { + return .hover + } + + return .enabled + } +} diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxNestedStyle.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxNestedStyle.swift new file mode 100644 index 000000000..5323abb61 --- /dev/null +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxNestedStyle.swift @@ -0,0 +1,54 @@ +// +// Software Name: OUDS iOS +// SPDX-FileCopyrightText: Copyright (c) Orange SA +// SPDX-License-Identifier: MIT +// +// This software is distributed under the MIT license, +// the text of which is available at https://opensource.org/license/MIT/ +// or see the "LICENSE" file for more details. +// +// Authors: See CONTRIBUTORS.txt +// Software description: A SwiftUI components library with code examples for Orange Unified Design System +// + +import OUDS +import OUDSTokensSemantic +import SwiftUI + +/// Just here to catch the isPressed state on the button +struct OUDSCheckboxNestedStyle: ButtonStyle { + + // MARK: - Properties + + let isOn: Bool // TODO: #264 - Manage three states + + @State private var isHover: Bool = false + @Environment(\.isEnabled) private var isEnabled + + // MARK: - Body + + func makeBody(configuration: Configuration) -> some View { + OUDSCheckboxSelectorButton(internalState: internalState(isPressed: configuration.isPressed), isOn: isOn) + .onHover { isHover in + self.isHover = isHover + } + } + + // MARK: - Helpers + + private func internalState(isPressed: Bool) -> InternalCheckboxState { + if !isEnabled { + return .disabled + } + + if isPressed { + return .pressed + } + + if isHover { + return .hover + } + + return .enabled + } +} diff --git a/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxSelectorButton.swift b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxSelectorButton.swift new file mode 100644 index 000000000..4292691b9 --- /dev/null +++ b/OUDS/Core/Components/Sources/Checkbox/Internal/OUDSCheckboxSelectorButton.swift @@ -0,0 +1,35 @@ +// +// Software Name: OUDS iOS +// SPDX-FileCopyrightText: Copyright (c) Orange SA +// SPDX-License-Identifier: MIT +// +// This software is distributed under the MIT license, +// the text of which is available at https://opensource.org/license/MIT/ +// or see the "LICENSE" file for more details. +// +// Authors: See CONTRIBUTORS.txt +// Software description: A SwiftUI components library with code examples for Orange Unified Design System +// + +import OUDS +import OUDSTokensSemantic +import SwiftUI + +/// The selector of the chebckox +struct OUDSCheckboxSelectorButton: View { + + // MARK: - Properties + + let internalState: InternalCheckboxState + let isOn: Bool // TODO: #264 Manage the three states + + @Environment(\.theme) private var theme + @Environment(\.colorScheme) private var colorScheme + + // MARK: - Body + + var body: some View { + // TODO: #264 Add selector + Text("[ ]") + } +} diff --git a/OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift b/OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift new file mode 100644 index 000000000..9da4bdfac --- /dev/null +++ b/OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift @@ -0,0 +1,91 @@ +// +// Software Name: OUDS iOS +// SPDX-FileCopyrightText: Copyright (c) Orange SA +// SPDX-License-Identifier: MIT +// +// This software is distributed under the MIT license, +// the text of which is available at https://opensource.org/license/MIT/ +// or see the "LICENSE" file for more details. +// +// Authors: See CONTRIBUTORS.txt +// Software description: A SwiftUI components library with code examples for Orange Unified Design System +// + +import OUDSFoundations +import SwiftUI + +// MARK: - OUDS Checkbox + +/// The ``OUDSCheckbox`` proposes a layout as a nested element. +/// It also proposes a more complex layout with text, icon and divider. +/// +/// ## Code samples +/// +/// TODO: #254 Add code samples +/// +/// ## Design documentation +/// +/// See [#TODO] +/// +/// - Since: 0.11.0 +public struct OUDSCheckbox: View { + + // MARK: - Properties + + private var isOn: Binding // TODO: #264 Add management of three states + private let layout: Layout + + // MARK: - Layout + + private enum Layout { + case `default`(OUDSCheckboxLabel.Label) + case inverse(OUDSCheckboxLabel.Label) + case selectorOnly + } + + // MARK: - Initializers + + /// Creates a checkbox with no label. + /// + /// - Parameter isOn: A binding to a property that determines wether the selector is ticked, unticked or preticked. + public init(isOn: Binding) { + self.isOn = isOn + self.layout = .selectorOnly + } + + /// Creates a checkbox with label and optional helper text, icon, divider. + /// + /// - Parameters: + /// - isOn: A binding to a property that determines wether the selector is ticked, unticker or preticked. + /// - label: The main label of the switch. + /// - helperText: An additonal helper text. + /// - icon: An optional icon + /// - onError: It the option is on error + /// - divider: If true a divider is added at the bottom of the view. + public init(isOn: Binding, label: String, helperText: String? = nil, icon: Image? = nil, onError: Bool = false, divider: Bool = false) { + self.isOn = isOn + self.layout = .default(.init(label: label, helperText: helperText, icon: icon, onError: onError, divider: divider)) + } + + // MARK: Body + + public var body: some View { + switch layout { + case .default(let label): + Button("") { + isOn.wrappedValue.toggle() + } + .buttonStyle(OUDSCheckboxLabeledStyle(isOn: isOn.wrappedValue, label: label, inverse: false)) + case .inverse(let label): + Button("") { + isOn.wrappedValue.toggle() + } + .buttonStyle(OUDSCheckboxLabeledStyle(isOn: isOn.wrappedValue, label: label, inverse: true)) + case .selectorOnly: + Button("") { + isOn.wrappedValue.toggle() + } + .buttonStyle(OUDSCheckboxNestedStyle(isOn: isOn.wrappedValue)) + } + } +} diff --git a/OUDS/Core/Components/Sources/_OUDSComponents.docc/OUDSComponents.md b/OUDS/Core/Components/Sources/_OUDSComponents.docc/OUDSComponents.md index 611dd299f..03e2931c1 100644 --- a/OUDS/Core/Components/Sources/_OUDSComponents.docc/OUDSComponents.md +++ b/OUDS/Core/Components/Sources/_OUDSComponents.docc/OUDSComponents.md @@ -54,6 +54,10 @@ if switch is in form it is possible to set it in error state. OUDSSwtcih(isOn $isOn, label: "Allow notifications", icon: Image("ic_heart"), onError: true, divider: true) ``` +### Checkbox + +TODO: #264 - Add doc + ## Customize components ### Apply a specific shadow effect (elevation tokens) @@ -134,3 +138,5 @@ The helper is available through `View`, and tokens through the provider of the t - ``OUDSButton`` - ``OUDSLink`` +- ``OUDSSwitch`` +- ``OUDSCheckbox``