Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Update `badge` to use internal icons for some status (Orange-OpenSource/ouds-ios#1136)
- `SwiftFormat` Swift package plugin from v0.58.5 to v0.58.6
- `actions/dependency-review-action` action from v4.8.1 to v4.8.2 for `dependency-review` workflow
- `color` semantic tokens (tokens libraries Core v1.9, System v2.2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,28 @@ struct BadgeCount: View {

// MARK: Stored properties

let count: UInt?
let size: OUDSBadge.StandardSize
let layout: BadgeLayout

@Environment(\.theme) private var theme

// MARK: Body

var body: some View {
if let count {
switch layout.type {
case .empty, .icon:
EmptyView()
case let .count(count, size):
let text = count > OUDSBadge.maxCount ? "+\(OUDSBadge.maxCount)" : "\(count)"
switch size {
case .extraSmall, .small:
EmptyView()
case .medium:
Text(text)
.labelDefaultSmall(theme)
.padding(.horizontal, horizontalPadding)
.padding(.horizontal, theme.badge.spacePaddingInlineMedium)
case .large:
Text(text)
.labelDefaultMedium(theme)
.padding(.horizontal, horizontalPadding)
.padding(.horizontal, theme.badge.spacePaddingInlineLarge)
}
}
}

private var horizontalPadding: SpaceSemanticToken {
switch size {
case .extraSmall, .small:
theme.spaces.fixedNone
case .medium:
theme.badge.spacePaddingInlineMedium
case .large:
theme.badge.spacePaddingInlineLarge
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,41 @@ struct BadgeIcon: View {

// MARK: Stored properties

let icon: Image?
let size: OUDSBadge.StandardSize
let layout: BadgeLayout

@Environment(\.theme) private var theme

// MARK: Body

var body: some View {
if let icon, validIconSize {
icon
.resizable()
switch layout.type {
case .empty, .count:
EmptyView()
case let .icon(icon, flipped, _):
(icon ?? defaultLeadingIcon)?
.renderingMode(.template)
.resizable()
.aspectRatio(contentMode: .fit)
.toFlip(flipped)
.padding(.all, theme.badge.spaceInset)
.accessibilityElement() // Otherwise label cannot be used in OUDSBadge body
}
}

private var validIconSize: Bool {
size == .medium || size == .large
// MARK: Helpers

private var defaultLeadingIcon: Image? {
switch layout.status {
case .neutral, .accent:
nil
case .positive:
Image(decorative: "ic_success", bundle: theme.resourcesBundle)
case .warning:
Image(decorative: "ic_warning_external_shape", bundle: theme.resourcesBundle)
case .negative:
Image(decorative: "ic_important", bundle: theme.resourcesBundle)
case .info:
Image(decorative: "ic_information", bundle: theme.resourcesBundle)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// 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 SwiftUI

/// The status of an `OUDSBadge` determines the leading element, the background
/// and the content colors of the tag according to the category.
struct BadgeLayout {

let type: Self.`Type`
let status: OUDSBadge.Status

/// The type element of the badge
enum `Type` {
/// Means no element on badge
case empty(size: OUDSBadge.StandardSize)

/// To display a count on badge
case count(value: UInt, size: OUDSBadge.IllustrationSize)

/// To display an icon. For `OUDSTag.Status.Category.neutral` and `OUDSTag.Status.Category.accent`
/// the decorative icon need to be provided. For other categories, a default icon is already provided.
case icon(customIcon: Image? = nil, flipIcon: Bool = false, size: OUDSBadge.IllustrationSize)
}

/// Internal initializer
///
/// - Parameters:
/// - type: The type of the badge
/// - status: The category of the status
init(type: Self.`Type`, status: OUDSBadge.Status) {
self.type = type
self.status = status
}

var size: OUDSBadge.StandardSize {
switch type {
case let .empty(size):
size
case let .count(_, illustrationSize),
let .icon(_, _, illustrationSize):
illustrationSize.standardSize
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//
// 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 OUDSTokensSemantic
import SwiftUI

struct BadgeModifier: ViewModifier {

// MARK: Properties

let layout: BadgeLayout
let accessibilityLabel: String

@Environment(\.theme) private var theme
@Environment(\.sizeCategory) private var sizeCategory: ContentSizeCategory
@Environment(\.isEnabled) private var isEnabled: Bool

// MARK: Body

func body(content: Content) -> some View {
content
.oudsForegroundColor(contentColor)
.frame(minWidth: frameSize, maxWidth: maxWidth, minHeight: frameSize, maxHeight: maxHeight, alignment: .center)
.oudsBackground(backgroundColor)
.clipShape(RoundedRectangle(cornerRadius: theme.borders.radiusPill))
.accessibilityHidden(accessibilityLabel.isEmpty)
.accessibilityLabel(accessibilityLabel)
}

// MARK: Private helpers

/// Returns the value to apply to compute frame wize.
/// If the text is not large, uses the expected tokens.
/// Otherwise uses the largest token and applies a factor based on the text size rate to have bigger size.
private var frameSize: SizeSemanticToken {
let rawSize = switch layout.size {
case .extraSmall:
theme.badge.sizeXsmall
case .small:
theme.badge.sizeSmall
case .medium:
theme.badge.sizeMedium
case .large:
theme.badge.sizeLarge
}
return rawSize * (sizeCategory.isLargeTextUsed ? sizeCategory.percentageRate / 100 : 1)
}

/// Returns the max width,
/// If count defined, i.e. means a text, don't limit width
private var maxWidth: CGFloat? {
if case .count = layout.type {
nil
} else {
frameSize
}
}

/// Returns the max height,
/// If count defined, i.e. means a text, don't limit width
private var maxHeight: CGFloat? {
if case .count = layout.type {
nil
} else {
frameSize
}
}

private var backgroundColor: MultipleColorSemanticTokens {
let enabbledColor = switch layout.status {
case .neutral:
theme.colors.surfaceInverseHigh
case .accent:
theme.colors.surfaceStatusAccentEmphasized
case .positive:
theme.colors.surfaceStatusPositiveEmphasized
case .info:
theme.colors.surfaceStatusInfoEmphasized
case .warning:
theme.colors.surfaceStatusWarningEmphasized
case .negative:
theme.colors.surfaceStatusNegativeEmphasized
}

return isEnabled ? enabbledColor : theme.colors.actionDisabled
}

private var contentColor: MultipleColorSemanticTokens {
let enabbledColor = switch layout.status {
case .neutral:
theme.colors.contentInverse
case .accent:
theme.colors.contentOnStatusAccentEmphasized
case .positive:
theme.colors.contentOnStatusPositiveEmphasized
case .info:
theme.colors.contentOnStatusInfoEmphasized
case .warning:
theme.colors.contentOnStatusWarningEmphasized
case .negative:
theme.colors.contentOnStatusNegativeEmphasized
}

return isEnabled ? enabbledColor : theme.colors.contentOnActionDisabled
}
}

extension OUDSBadge.IllustrationSize {

/// Internal usage: convert to standard size
var standardSize: OUDSBadge.StandardSize {
switch self {
case .medium:
.medium
case .large:
.large
}
}
}

extension OUDSBadge.StatusWithIcon {

var status: OUDSBadge.Status {
switch self {
case .neutral:
.neutral
case .accent:
.accent
case .positive:
.positive
case .info:
.info
case .warning:
.warning
case .negative:
.negative
}
}

var icon: (image: Image, flipped: Bool)? {
switch self {
case let .neutral(icon, flipped):
(icon, flipped)
case let .accent(icon, flipped):
(icon, flipped)
default: nil

Check failure on line 155 in OUDS/Core/Components/Sources/Indicators/Badge/Internal/BadgeModifier.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cases on Newline (switch_case_on_newline)

Cases inside a switch should always be on a newline

Check failure on line 155 in OUDS/Core/Components/Sources/Indicators/Badge/Internal/BadgeModifier.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cases on Newline (switch_case_on_newline)

Cases inside a switch should always be on a newline
}
}
}
Loading
Loading