Skip to content

Commit

Permalink
chore: duplicate implementation of switch for checkbox component (#264)
Browse files Browse the repository at this point in the history
Signed-off-by: Pierre-Yves Lapersonne <[email protected]>
  • Loading branch information
pylapp committed Feb 4, 2025
1 parent c2f93f9 commit 3c2f553
Show file tree
Hide file tree
Showing 7 changed files with 376 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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("[ ]")
}
}
91 changes: 91 additions & 0 deletions OUDS/Core/Components/Sources/Checkbox/OUDSCheckbox.swift
Original file line number Diff line number Diff line change
@@ -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<Bool> // 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<Bool>) {
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<Bool>, 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))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -134,3 +138,5 @@ The helper is available through `View`, and tokens through the provider of the t

- ``OUDSButton``
- ``OUDSLink``
- ``OUDSSwitch``
- ``OUDSCheckbox``

0 comments on commit 3c2f553

Please sign in to comment.