Skip to content

Commit

Permalink
feat: 🎸 [HCPSDKFIORIUIKIT-2481] New Mobile Card (#665)
Browse files Browse the repository at this point in the history
* feat: 🎸 [HCPSDKFIORIUIKIT-2481] New Mobile Card

* fix: 🐛 [HCPSDKFIORIUIKIT-2481] remove one public TagStyle
  • Loading branch information
shengxu7 committed Mar 18, 2024
1 parent a214ead commit df4a0b9
Show file tree
Hide file tree
Showing 23 changed files with 1,157 additions and 600 deletions.
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "MicrosoftTeams-image.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
803 changes: 393 additions & 410 deletions Apps/Examples/Examples/FioriSwiftUICore/Card/MobileCardExample.swift

Large diffs are not rendered by default.

18 changes: 12 additions & 6 deletions Sources/FioriSwiftUICore/FioriButton/FioriButtonStyle.swift
Expand Up @@ -75,11 +75,13 @@ public struct FioriPlainButtonStyle: FioriButtonStyle {

/// A Fiori button style for the primary button.
public struct FioriPrimaryButtonStyle: FioriButtonStyle {
private let maxWidth: CGFloat?

/// Create a `FioriPrimaryButtonStyle` instance.
public init() {}
public init(_ maxWidth: CGFloat? = nil) { self.maxWidth = maxWidth }

public func makeBody(configuration: Configuration) -> some View {
let config = FioriButtonStyleProvider.getPrimaryButtonStyle(state: configuration.state)
let config = FioriButtonStyleProvider.getPrimaryButtonStyle(state: configuration.state).withMaxWidth(self.maxWidth)

return configuration.label
.fioriButtonConfiguration(config)
Expand All @@ -89,15 +91,17 @@ public struct FioriPrimaryButtonStyle: FioriButtonStyle {
/// A Fiori button style for the secondary button.
public struct FioriSecondaryButtonStyle: FioriButtonStyle {
private let colorStyle: FioriButtonColorStyle
private let maxWidth: CGFloat?

/// Create a `FioriSecondaryButtonStyle` instance.
/// - Parameter colorStyle: The color style used for this button style.
public init(colorStyle: FioriButtonColorStyle = .tint) {
public init(colorStyle: FioriButtonColorStyle = .tint, maxWidth: CGFloat? = nil) {
self.colorStyle = colorStyle
self.maxWidth = maxWidth
}

public func makeBody(configuration: Configuration) -> some View {
let config = FioriButtonStyleProvider.getSecondaryButtonStyle(colorStyle: self.colorStyle, for: configuration.state)
let config = FioriButtonStyleProvider.getSecondaryButtonStyle(colorStyle: self.colorStyle, for: configuration.state).withMaxWidth(self.maxWidth)

return configuration.label
.fioriButtonConfiguration(config)
Expand All @@ -107,15 +111,17 @@ public struct FioriSecondaryButtonStyle: FioriButtonStyle {
/// A Fiori button style for the tertiary button.
public struct FioriTertiaryButtonStyle: FioriButtonStyle {
private let colorStyle: FioriButtonColorStyle
private let maxWidth: CGFloat?

/// Create a `FioriTertiaryButtonStyle` instance.
/// - Parameter colorStyle: The color style used for this button style.
public init(colorStyle: FioriButtonColorStyle = .tint) {
public init(colorStyle: FioriButtonColorStyle = .tint, maxWidth: CGFloat? = nil) {
self.colorStyle = colorStyle
self.maxWidth = maxWidth
}

public func makeBody(configuration: Configuration) -> some View {
let config = FioriButtonStyleProvider.getTertiaryButtonStyle(colorStyle: self.colorStyle, for: configuration.state)
let config = FioriButtonStyleProvider.getTertiaryButtonStyle(colorStyle: self.colorStyle, for: configuration.state).withMaxWidth(self.maxWidth)

return configuration.label
.fioriButtonConfiguration(config)
Expand Down
Expand Up @@ -131,12 +131,18 @@ struct FioriButtonConfiguration {
let backgroundColor: Color
let font: Font
let padding: EdgeInsets
let maxWidth: CGFloat?

init(foregroundColor: Color, backgroundColor: Color, font: Font = .fiori(forTextStyle: .body).weight(.bold), padding: EdgeInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)) {
init(foregroundColor: Color, backgroundColor: Color, font: Font = .fiori(forTextStyle: .body).weight(.bold), padding: EdgeInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16), maxWidth: CGFloat? = nil) {
self.foregroundColor = foregroundColor
self.backgroundColor = backgroundColor
self.font = font
self.padding = padding
self.maxWidth = maxWidth
}

func withMaxWidth(_ maxWidth: CGFloat?) -> FioriButtonConfiguration {
FioriButtonConfiguration(foregroundColor: self.foregroundColor, backgroundColor: self.backgroundColor, font: self.font, padding: self.padding, maxWidth: maxWidth)
}
}

Expand All @@ -146,8 +152,8 @@ extension View {
.font(config.font)
.foregroundColor(config.foregroundColor)
.padding(config.padding)
.frame(minWidth: 44, maxWidth: config.maxWidth, minHeight: 44)
.background(RoundedRectangle(cornerRadius: 5).fill(config.backgroundColor))
.frame(minWidth: 44, minHeight: 44)
.contentShape(Rectangle())
}
}
@@ -0,0 +1,97 @@
import SwiftUI

struct EqualWidthWithMaxWidthHStackLayout: Layout {
/// The distance between adjacent subviews.
var spacing: CGFloat? = 4

/// Same height for all subviews
var isSameHeight: Bool = true

/// The vertical alignment of subviews.
var alignment: VerticalAlignment = .center

/// Maximum width for each element in the container
var maxWidth: CGFloat? = nil

var horizontalSizeClass: UserInterfaceSizeClass? = .compact

func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
guard let containerWidth = proposal.width, containerWidth > 0, !subviews.isEmpty else { return .zero }
let subViewSizes = subviews.map {
$0.sizeThatFits(.unspecified)
}
let maxHeight: CGFloat = subViewSizes.reduce(0) { curr, subviewSize in
max(curr, subviewSize.height)
}

return CGSize(width: containerWidth, height: maxHeight)
}

func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
guard let containerWidth = proposal.width, containerWidth > 0, !subviews.isEmpty else { return }

let subViewSizes = subviews.map {
$0.sizeThatFits(.unspecified)
}
let maxHeight: CGFloat = subViewSizes.reduce(0) { curr, subviewSize in
max(curr, subviewSize.height)
}
let theSpacing: CGFloat = self.spacing ?? 0
let totalSpacing = theSpacing * CGFloat(subviews.count - 1)
let theMaxWidth = self.horizontalSizeClass == .compact ? CGFloat.greatestFiniteMagnitude : self.maxWidth ?? CGFloat.greatestFiniteMagnitude
let subviewWidth: CGFloat = min(theMaxWidth, (containerWidth - CGFloat(subviews.count - 1) * theSpacing) / CGFloat(subviews.count))

// align trailing
var x: CGFloat = bounds.minX + subviewWidth / 2 + bounds.size.width - subviewWidth * CGFloat(subviews.count) - totalSpacing
var y = bounds.midY
var anchor = UnitPoint.center
switch self.alignment {
case .top:
y = bounds.minY
anchor = .top
case .bottom:
y = bounds.maxY
anchor = .bottom
default:
y = bounds.midY
anchor = .center
}
for i in subviews.indices {
subviews[i].place(at: CGPointMake(x, y),
anchor: anchor,
proposal: ProposedViewSize(width: subviewWidth, height: self.isSameHeight ? maxHeight : subViewSizes[i].height))
x += subviewWidth + theSpacing
}
}
}

#Preview("layout top") {
EqualWidthWithMaxWidthHStackLayout(spacing: 8, alignment: .top) {
FioriButton(title: "Save")
.border(Color.green)
Text("Decline")
.border(Color.green)
}.border(Color.gray)
}

#Preview("layout center") {
EqualWidthWithMaxWidthHStackLayout(spacing: 8, alignment: .center) {
FioriButton(title: "Save")
.frame(maxWidth: .infinity)
.border(Color.green)
Text("Decline")
.border(Color.green)
}.border(Color.gray)
}

#Preview("layout bottom") {
EqualWidthWithMaxWidthHStackLayout(spacing: 8, alignment: .bottom, maxWidth: 120) {
FioriButton(title: "Save")
.frame(maxWidth: .infinity)
.border(Color.green)

Text("Decline")
.frame(maxWidth: .infinity)
.border(Color.green)
}.border(Color.gray)
}
28 changes: 28 additions & 0 deletions Sources/FioriSwiftUICore/Utils/OptionalImage.swift
@@ -0,0 +1,28 @@
import SwiftUI

struct OptionalImage: View {
let image: Image?

init(_ image: Image?) {
self.image = image
}

var body: some View {
if let image {
image
.resizable()
} else {
EmptyView()
}
}
}

extension OptionalImage: _ViewEmptyChecking {
var isEmpty: Bool {
self.image == nil
}
}

#Preview {
OptionalImage(Image(systemName: "square.and.arrow.up.circle"))
}
30 changes: 30 additions & 0 deletions Sources/FioriSwiftUICore/Utils/OptionalKPIItem.swift
@@ -0,0 +1,30 @@
import SwiftUI

struct OptionalKPIItem: View {
let data: KPIItemData?

init(_ data: KPIItemData?) {
self.data = data
}

var body: some View {
if let d = data {
KPIItem(data: d)
} else {
EmptyView()
}
}
}

extension OptionalKPIItem: _ViewEmptyChecking {
var isEmpty: Bool {
self.data == nil
}
}

#Preview {
OptionalKPIItem(KPIItemData.components([.icon(Image(systemName: "arrowtriangle.up.fill")),
.unit("$"),
.metric("26.9"),
.unit("M")]))
}
4 changes: 2 additions & 2 deletions Sources/FioriSwiftUICore/Views/TagStack/Tag.swift
Expand Up @@ -42,7 +42,7 @@ public struct LightTagStyle: TagStyle {
.font(.fiori(forTextStyle: .footnote))
.foregroundColor(.preferredColor(.secondaryLabel))
.padding(EdgeInsets(top: 2, leading: 3, bottom: 2, trailing: 3))
.background(RoundedRectangle(cornerRadius: 4).stroke(Color.preferredColor(.quarternaryLabel), lineWidth: 0.5))
.background(RoundedRectangle(cornerRadius: 4).stroke(Color.preferredColor(.quaternaryLabel), lineWidth: 0.5))
}
}

Expand Down Expand Up @@ -79,7 +79,7 @@ public struct CustomTagStyle: TagStyle {
var borderWidth: CGFloat = 0.5

/// Color around the perimeter of the tag
var borderColor: Color = .preferredColor(.quarternaryLabel)
var borderColor: Color = .preferredColor(.quaternaryLabel)

public init(textColor: Color? = nil, font: Font? = nil, fillColor: Color? = nil, contentInsets: EdgeInsets? = nil, cornerRadius: CGFloat? = nil, borderWidth: CGFloat? = nil, borderColor: Color? = nil) {
if let tc = textColor {
Expand Down
Expand Up @@ -144,13 +144,13 @@ protocol _CardBodyComponent {

// sourcery: BaseComponent
protocol _MediaImageComponent {
// sourcery: @ViewBuilder
// sourcery: @ViewBuilder, resultBuilder.backingComponent = OptionalImage
var mediaImage: Image? { get }
}

// sourcery: BaseComponent
protocol _KpiComponent {
// sourcery: @ViewBuilder, resultBuilder.backingComponent = KPIItem
// sourcery: @ViewBuilder, resultBuilder.backingComponent = OptionalKPIItem
var kpi: KPIItemData? { get }
}

Expand Down

0 comments on commit df4a0b9

Please sign in to comment.