Skip to content

Commit

Permalink
refactor: fix some design issues and mutualize some UI elements in de…
Browse files Browse the repository at this point in the history
…mo app (#189) (#194)

Reviewed-by: Pierre-Yves Lapersonne <pierreyves.lapersonne@orange.com>
ludovic35 authored Oct 14, 2024
1 parent e075720 commit 77e437d
Showing 11 changed files with 210 additions and 135 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- [DemoApp] Fix some design issues and mutualize some UI elements ([#189](https://github.com/Orange-OpenSource/ouds-ios/issues/189))
- [DemoApp] Add sizing token screen in demo app ([#150](https://github.com/Orange-OpenSource/ouds-ios/issues/150))
- [DemoApp] Add spacing token screen in demo app ([#149](https://github.com/Orange-OpenSource/ouds-ios/issues/149))
- [Library] Add color semantic tokens `colorContentTransparentDefault`, `colorBorderTransparentDefault` and `colorBackgroundTransparentDefault` (October 8th) ([#177](https://github.com/Orange-OpenSource/ouds-ios/issues/177))
4 changes: 4 additions & 0 deletions Showcase/Showcase.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@
0747947A2CAE882A0033C2D8 /* EmptyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074794792CAE882A0033C2D8 /* EmptyState.swift */; };
075114DB2CB7D28D00B8B759 /* SizingTokenElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075114D82CB7D28D00B8B759 /* SizingTokenElement.swift */; };
075114DC2CB7D28D00B8B759 /* SizingTokenPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075114D92CB7D28D00B8B759 /* SizingTokenPage.swift */; };
075114E82CB81F0E00B8B759 /* ShowcaseTokenIllustration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075114E72CB81F0E00B8B759 /* ShowcaseTokenIllustration.swift */; };
077CCE572CB426090059CC28 /* SpacingTokenElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077CCE512CB426090059CC28 /* SpacingTokenElement.swift */; };
077CCE582CB426090059CC28 /* SpacingTokenPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077CCE522CB426090059CC28 /* SpacingTokenPage.swift */; };
077CCE592CB426090059CC28 /* DimensionTokenElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077CCE542CB426090059CC28 /* DimensionTokenElement.swift */; };
@@ -102,6 +103,7 @@
074794792CAE882A0033C2D8 /* EmptyState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyState.swift; sourceTree = "<group>"; };
075114D82CB7D28D00B8B759 /* SizingTokenElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SizingTokenElement.swift; sourceTree = "<group>"; };
075114D92CB7D28D00B8B759 /* SizingTokenPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SizingTokenPage.swift; sourceTree = "<group>"; };
075114E72CB81F0E00B8B759 /* ShowcaseTokenIllustration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowcaseTokenIllustration.swift; sourceTree = "<group>"; };
077CCE512CB426090059CC28 /* SpacingTokenElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpacingTokenElement.swift; sourceTree = "<group>"; };
077CCE522CB426090059CC28 /* SpacingTokenPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpacingTokenPage.swift; sourceTree = "<group>"; };
077CCE542CB426090059CC28 /* DimensionTokenElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DimensionTokenElement.swift; sourceTree = "<group>"; };
@@ -196,6 +198,7 @@
51E3FF0A2CAFD9AE00F1BC59 /* ShowcaseElementPage.swift */,
073543152CA17275001187EA /* ShowcaseElement.swift */,
077CCE5B2CB431C50059CC28 /* ShowcaseVariantElement.swift */,
075114E72CB81F0E00B8B759 /* ShowcaseTokenIllustration.swift */,
);
path = Utils;
sourceTree = "<group>";
@@ -691,6 +694,7 @@
0735431B2CA18C48001187EA /* TypographyTokenPage.swift in Sources */,
51BD76222C466FCF0033365D /* ComponentsPage.swift in Sources */,
07CF42892CA55BC8000BD03E /* ThemeSelection.swift in Sources */,
075114E82CB81F0E00B8B759 /* ShowcaseTokenIllustration.swift in Sources */,
07CF427F2CA41325000BD03E /* OpacityTokenElement.swift in Sources */,
07CF426B2CA30728000BD03E /* TokensPage.swift in Sources */,
073543112CA154DE001187EA /* Card.swift in Sources */,
142 changes: 84 additions & 58 deletions Showcase/Showcase/Pages/Tokens/Border/BorderTokenPage.swift
Original file line number Diff line number Diff line change
@@ -26,86 +26,98 @@ struct BorderTokenPage: View {
// MARK: Body

var body: some View {
VStack(alignment: .center, spacing: theme.spaceFixedMedium) {
groupOfIllustrations(name: "Width") {
ForEach(Width.allCases, id: \.rawValue) { width in
let token = width.token(from: theme)

illustration(width: token,
radius: theme.borderRadiusNone,
style: theme.borderStyleDefault,
name: width.rawValue,
value: token)
VStack(alignment: .leading, spacing: theme.spaceFixedMedium) {
Section {
VStack(alignment: .leading, spacing: theme.spaceFixedNone) {
ForEach(NamedBorderWidth.allCases, id: \.rawValue) { namedWidth in
illustration(for: namedWidth)
}
}
} header: {
Text("Width")
.typeHeadingLarge(theme)
.foregroundStyle((theme.colorContentDefault?.color(for: colorScheme))!)
}

groupOfIllustrations(name: "Radius") {
ForEach(Radius.allCases, id: \.rawValue) { radius in
let token = radius.token(from: theme)
illustration(width: theme.borderWidthDefault,
radius: token,
style: theme.borderStyleDefault,
name: radius.rawValue,
value: token)
Section {
VStack(alignment: .leading, spacing: theme.spaceFixedNone) {
ForEach(NamedBorderRadius.allCases, id: \.rawValue) { namedRadius in
illustration(for: namedRadius)
}
}
} header: {
Text("Radius")
.typeHeadingLarge(theme)
.foregroundStyle((theme.colorContentDefault?.color(for: colorScheme))!)
}

groupOfIllustrations(name: "Style") {
illustration(width: theme.borderWidthMedium,
radius: theme.borderRadiusNone,
style: theme.borderStyleDefault,
name: "borderStyleDefault")
illustration(width: theme.borderWidthMedium,
radius: theme.borderRadiusNone,
style: theme.borderStyleDrag,
name: "borderStyleDrag")
Section {
VStack(alignment: .leading, spacing: theme.spaceFixedNone) {
ForEach(NamedBorderStyle.allCases, id: \.rawValue) { namedStyle in
illustration(for: namedStyle)
}
}
} header: {
Text("Style")
.typeHeadingLarge(theme)
.foregroundStyle((theme.colorContentDefault?.color(for: colorScheme))!)
}
}
.padding(.horizontal, theme.spaceFixedMedium)
}

// MARK: Private helpers

@ViewBuilder
private func groupOfIllustrations<Illustration> (name: String, @ViewBuilder illustrations: () -> Illustration) -> some View where Illustration: View {
VStack(alignment: .leading, spacing: theme.spaceFixedShorter) {
Text(name).typeHeadingLarge(theme)
private var rectangle: some View {
Rectangle()
.fill(theme.colorBackgroundDefaultSecondary?.color(for: colorScheme) ?? Color(UIColor.systemGroupedBackground))
.frame(width: 64, height: 64)
}

VStack(alignment: .leading, spacing: theme.spaceFixedNone) {
illustrations()
}
private func illustration(for namedWidth: NamedBorderWidth) -> some View {
let token = namedWidth.token(from: theme)
let name = namedWidth.rawValue
let value = String(format: "(%.0f) pt", token)

return ShowcaseTokenIllustration(tokenName: name, tokenValue: value) {
rectangle
.oudsBorder(style: theme.borderStyleDefault,
width: token,
radius: theme.borderRadiusNone,
color: theme.colorBorderDefault!)
}
}

private func illustration(
width: BorderWidthSemanticToken,
radius: BorderRadiusSemanticToken,
style: BorderStyleSemanticToken,
name: String,
value: Double? = nil) -> some View {
HStack(alignment: .center, spacing: theme.spaceFixedMedium) {

Rectangle()
.fill(theme.colorBackgroundDefaultSecondary?.color(for: colorScheme) ?? Color(UIColor.systemGroupedBackground))
.frame(width: 64, height: 64)
.oudsBorder(style: style,
width: width,
radius: radius,
private func illustration(for namedRadius: NamedBorderRadius) -> some View {
let token = namedRadius.token(from: theme)
let name = namedRadius.rawValue
let value = String(format: "(%.0f) pt", token)

return ShowcaseTokenIllustration(tokenName: name, tokenValue: value) {
rectangle
.oudsBorder(style: theme.borderStyleDefault,
width: theme.borderWidthDefault,
radius: token,
color: theme.colorBorderDefault!)
}
}

VStack(alignment: .leading, spacing: theme.spaceFixedNone) {
Text(name).bold()
if let value {
Text("\(value, specifier: "%.1f")")
}
}
.frame(maxWidth: .infinity, alignment: .leading)
private func illustration(for namedStyle: NamedBorderStyle) -> some View {
let token = namedStyle.token(from: theme)
let name = namedStyle.rawValue
let value = token

return ShowcaseTokenIllustration(tokenName: name, tokenValue: value) {
rectangle
.oudsBorder(style: token,
width: theme.borderWidthDefault,
radius: theme.borderRadiusNone,
color: theme.colorBorderDefault!)
}
.padding(.vertical, theme.spaceFixedShorter)
}
}

private enum Radius: String, CaseIterable {
private enum NamedBorderRadius: String, CaseIterable {
case borderRadiusNone
case borderRadiusDefault
case borderRadiusShort
@@ -128,7 +140,7 @@ private enum Radius: String, CaseIterable {
}
}

private enum Width: String, CaseIterable {
private enum NamedBorderWidth: String, CaseIterable {

case borderWidthNone
case borderWidthDefault
@@ -157,3 +169,17 @@ private enum Width: String, CaseIterable {
}
}
}

private enum NamedBorderStyle: String, CaseIterable {
case borderStyleDefault
case borderStyleDrag

func token(from theme: OUDSTheme) -> BorderStyleSemanticToken {
switch self {
case .borderStyleDefault:
return theme.borderStyleDefault
case .borderStyleDrag:
return theme.borderStyleDrag
}
}
}
Original file line number Diff line number Diff line change
@@ -36,12 +36,12 @@ struct SizingTokenPage: View {

// MARK: Private helpers

private func illustration(for sizingName: NamedSizing) -> some View {
illustration(for: sizingName.token(from: theme), named: sizingName.rawValue)
}
private func illustration(for namedSizing: NamedSizing) -> some View {
let token = namedSizing.token(from: theme)
let name = namedSizing.rawValue
let value = String(format: "(%.0f) pt", token)

private func illustration(for sizingSementicToken: SizingSemanticToken, named: String) -> some View {
HStack(alignment: .center, spacing: theme.spaceFixedMedium) {
return ShowcaseTokenIllustration(tokenName: name, tokenValue: value) {
ZStack {
Rectangle()
.fill((theme.colorBackgroundEmphasizedPrimary?.color(for: colorScheme))!)
@@ -52,17 +52,9 @@ struct SizingTokenPage: View {
.renderingMode(.template)
.foregroundColor(.blue)
// .foregroundColor((theme.colorBackgroundStatusAttractiveEmphasized?.color(for: colorScheme))!) // TODO: Update when color is available
.frame(width: sizingSementicToken, height: sizingSementicToken, alignment: .center)
}

VStack(alignment: .leading) {
Text(named).typeBodyStrongLarge(theme)
Text("(\(sizingSementicToken, specifier: "%.0f")) pt")
.frame(width: token, height: token, alignment: .center)
}
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(theme.colorContentDefault?.color(for: colorScheme))
}
.padding(.vertical, theme.spaceFixedShorter)
}
}

Original file line number Diff line number Diff line change
@@ -36,33 +36,25 @@ struct SpacingTokenPage: View {

// MARK: Private helpers

private func illustration(for spacingName: NamedSpacing) -> some View {
illustration(for: spacingName.token(from: theme), named: spacingName.rawValue)
}

private func illustration(for spacingSementicToken: MultipleSpacingTokens, named: String) -> some View {
private func illustration(for namedSpacing: NamedSpacing) -> some View {
let token = namedSpacing.token(from: theme)
let name = namedSpacing.rawValue
let horizontalDimensionRawToken = token.dimension(for: horizontalSizeClass ?? .regular)
let value = String(format: "horizontal %@\n %.0f pt",
horizontalSizeClass == .regular ? "regular" : "compact",
horizontalDimensionRawToken)

let horizontalDimensionRawToken = spacingSementicToken.dimension(for: horizontalSizeClass ?? .regular)
let verticalDimensionRawToken = spacingSementicToken.dimension(for: verticalSizeClass ?? .regular)
return HStack(alignment: .center, spacing: theme.spaceFixedMedium) {
return ShowcaseTokenIllustration(tokenName: name, tokenValue: value) {
ZStack {
Rectangle()
.fill((theme.colorBackgroundEmphasizedPrimary?.color(for: colorScheme))!)
.frame(width: 64, height: 64, alignment: .center)
Rectangle()
// .fill((theme.colorBackgroundStatusAttractiveEmphasized?.color(for: colorScheme))!) // TODO: Update when color is available
// .fill((theme.colorBackgroundStatusAttractiveEmphasized?.color(for: colorScheme))!) // TODO: Update when color is available
.fill(.blue)
.frame(width: horizontalDimensionRawToken, height: 64, alignment: .center)
}
VStack(alignment: .leading) {
Text(named).typeBodyStrongLarge(theme)
Text("horizontal \(horizontalSizeClass == .regular ? "regular" : "compact") \(horizontalDimensionRawToken, specifier: "%.0f") pt")
Text("vertical \(verticalSizeClass == .regular ? "regular" : "compact") \(verticalDimensionRawToken, specifier: "%.0f") pt")
}
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(theme.colorContentDefault?.color(for: colorScheme))
}
.padding(.vertical, theme.spaceFixedShorter)
}
}

37 changes: 11 additions & 26 deletions Showcase/Showcase/Pages/Tokens/Elevation/ElevationTokenPage.swift
Original file line number Diff line number Diff line change
@@ -37,34 +37,19 @@ struct ElevationTokenPage: View {

// MARK: Helpers

private func illustration(for elevation: NamedElevation) -> some View {
illustration(for: elevation.token(from: theme), named: elevation.rawValue)
}

private func illustration(for elevationSementicToken: ElevationCompositeSemanticToken, named: String) -> some View {

let elevationRawToken = elevationSementicToken.elevation(for: colorScheme)
return HStack(alignment: .center, spacing: theme.spaceFixedMedium) {
elevationRectangle(elevationRawToken: elevationRawToken)

VStack(alignment: .leading) {
Text(named).bold()
Text("x: \(elevationRawToken.x, specifier: "%.2f"), ")
+ Text("y: \(elevationRawToken.y, specifier: "%.2f"), ")
+ Text("radius: \(elevationRawToken.radius, specifier: "%.2f")")
private func illustration(for namedElevation: NamedElevation) -> some View {
let token = namedElevation.token(from: theme).elevation(for: colorScheme)
let name = namedElevation.rawValue
let value = String(format: "x: %.2f, y: %.2f, radius: %.2f\nColor: %@", token.x, token.y, token.radius, token.color)

return ShowcaseTokenIllustration(tokenName: name, tokenValue: value) {
Rectangle()
.frame(width: theme.sizeIconDecorativeTallest, height: theme.sizeIconDecorativeTallest)
.foregroundColor(theme.colorBackgroundDefaultSecondary?.color(for: colorScheme))
.shadow(elevation: token)
.padding(.bottom, 2)

Text("Color: \(elevationRawToken.color)")
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(.vertical, theme.spaceFixedShorter)
}

private func elevationRectangle(elevationRawToken: ElevationCompositeRawToken) -> some View {
Rectangle()
.frame(width: theme.sizeIconDecorativeTallest, height: theme.sizeIconDecorativeTallest)
.foregroundColor(theme.colorBackgroundDefaultSecondary?.color(for: colorScheme))
.shadow(elevation: elevationRawToken)
}
}

43 changes: 27 additions & 16 deletions Showcase/Showcase/Pages/Tokens/Opacity/OpacityTokenPage.swift
Original file line number Diff line number Diff line change
@@ -38,34 +38,45 @@ struct OpacityTokenPage: View {
// MARK: Helpers

private func illustration(for opacityName: NamedOpacity) -> some View {
illustration(for: opacityName.token(from: theme), named: opacityName.rawValue)
}
let token = opacityName.token(from: theme)
let name = opacityName.rawValue
let value = String(format: "%.2f", token)

private func illustration(for opacityToken: OpacitySemanticToken, named name: String) -> some View {
HStack(alignment: .center, spacing: theme.spaceFixedMedium) {
return ShowcaseTokenIllustration(tokenName: name, tokenValue: value) {
ZStack {
Image("ic_union")
.resizable()
.frame(width: 44, height: 44)

Rectangle().fill(colorScheme == .dark ? .white : .black)
.opacity(opacityToken)
Rectangle()
.fill((theme.colorContentDefault?.color(for: colorScheme))!)
.opacity(token)
.frame(width: 44, height: 44)
.oudsBorder(style: theme.borderStyleDefault,
width: theme.borderWidthThin,
radius: theme.borderRadiusNone,
color: theme.colorContentDefault!)
.modifier(ExtraBorderModifier(namedOpacity: opacityName))
.transformEffect(CGAffineTransform(translationX: 10, y: 10))
}
.frame(width: 54, height: 54, alignment: .leading)
}
}
}

VStack(alignment: .leading, spacing: theme.spaceFixedNone) {
Text(name).bold()
Text("\(opacityToken, specifier: "%.2f")")
}
.frame(maxWidth: .infinity, alignment: .leading)
// MARK: - Extra border modifier

// Add a border for transparent token
private struct ExtraBorderModifier: ViewModifier {

@Environment(\.theme) private var theme
let namedOpacity: NamedOpacity

func body(content: Content) -> some View {
if namedOpacity == .opacityTransparent {
content.oudsBorder(style: theme.borderStyleDefault,
width: theme.borderWidthThin,
radius: theme.borderRadiusNone,
color: theme.colorContentDefault!)
} else {
content
}
.padding(.vertical, theme.spaceFixedShorter)
}
}

Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import SwiftUI
struct TypographyTokenPage: View {

@Environment(\.theme) private var theme
@Environment(\.colorScheme) private var colorScheme

// MARK: Body

@@ -40,23 +41,26 @@ struct TypographyTokenPage: View {
let token = namedTypography.token(from: theme).compact

VStack(alignment: .leading, spacing: theme.spaceFixedNone) {
typgraphyName(from: namedTypography)
typgraphyIllustration(from: namedTypography)
.foregroundStyle((theme.colorContentMuted?.color(for: colorScheme))!)

Group {
Text("family (\(theme.customFontFamily ?? "system")), ")
+ Text("weight (\(token.weight)), ")
+ Text("size (\(token.size, specifier: "%.2f")), ")
+ Text("lineHeight (\(token.lineHeight, specifier: "%.2f")), ")
+ Text("letterSpacing \(token.letterSpacing, specifier: "%.2f"))")
}
.typeBodyDefaultSmall(theme)
.typeBodyDefaultMedium(theme)
.fixedSize(horizontal: false, vertical: true)
.foregroundStyle((theme.colorContentDefault?.color(for: colorScheme))!)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, theme.spaceFixedShorter)
}

@ViewBuilder
private func typgraphyName(from namedTypography: NamedTypography) -> some View {
private func typgraphyIllustration(from namedTypography: NamedTypography) -> some View {
switch namedTypography {
case .displayLarge:
Text(namedTypography.rawValue).typeDisplayLarge(theme)
2 changes: 1 addition & 1 deletion Showcase/Showcase/Pages/Utils/ShowcaseElementPage.swift
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ struct ShowcaseElementPage: View {
.accessibilityHidden(true)

Text(LocalizedStringKey(element.description))
.typeBodyDefaultMedium(theme)
.typeBodyDefaultLarge(theme)
.accessibilityFocused($requestFocus)
.padding(.horizontal, theme.spaceFixedMedium)

59 changes: 59 additions & 0 deletions Showcase/Showcase/Pages/Utils/ShowcaseTokenIllustration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// 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
import OUDS
import OUDSTokensSemantic

struct ShowcaseTokenIllustration<TokenIllustration>: View where TokenIllustration: View {

@Environment(\.theme) private var theme
@Environment(\.colorScheme) private var colorScheme

// MARK: Stored properties

@ViewBuilder
let tokenIllustration: () -> TokenIllustration
let tokenName: String
let tokenValue: String?

// MARK: Initializer

init(tokenName: String, tokenValue: String? = nil, tokenIllustration: @escaping () -> TokenIllustration) {
self.tokenIllustration = tokenIllustration
self.tokenName = tokenName
self.tokenValue = tokenValue
}

// MARK: Body

var body: some View {
HStack(alignment: .top, spacing: theme.spaceFixedMedium) {

tokenIllustration()

VStack(alignment: .leading) {
Text(tokenName)
.typeBodyStrongLarge(theme)
.foregroundStyle((theme.colorContentDefault?.color(for: colorScheme))!)
if let tokenValue {
Text(tokenValue)
.typeBodyDefaultMedium(theme)
.foregroundStyle((theme.colorContentMuted?.color(for: colorScheme))!)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(.vertical, theme.spaceFixedShorter)
}
}
1 change: 1 addition & 0 deletions Showcase/Showcase/Pages/Utils/ShowcaseVariantElement.swift
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ struct ShowcaseVariantElement: View {
} label: {
Text(LocalizedStringKey(element.name))
.typeHeadingMedium(theme)
.foregroundStyle((theme.colorContentDefault?.color(for: colorScheme))!)
.padding(.vertical, theme.spaceFixedShorter)
}
}

0 comments on commit 77e437d

Please sign in to comment.