Skip to content

Commit

Permalink
refactor: 💡 [JIRA:HCPSDKFIORIUIKIT-2880] ActionItems Refactor (#939)
Browse files Browse the repository at this point in the history
* refactor: 💡 [JIRA:HCPSDKFIORIUIKIT-2880] ActionItems Refactor

* refactor: 💡 [JIRA:HCPSDKFIORIUIKIT-2880] ActionItems Refactor

* refactor: 💡 [JIRA:HCPSDKFIORIUIKIT-2880] ActionItems Refactor

Support fiori style for action items

* refactor: 💡 [JIRA:HCPSDKFIORIUIKIT-2880] ActionItems Refactor

Doc grammer fix
  • Loading branch information
hengyi-zhang authored Dec 25, 2024
1 parent 6ca89fa commit 33d3788
Show file tree
Hide file tree
Showing 17 changed files with 523 additions and 2 deletions.
12 changes: 12 additions & 0 deletions Apps/Examples/Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
6D6E866D2C53969A00EDB6F4 /* CardTwoButtonsChangeToOneExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6E866C2C53969A00EDB6F4 /* CardTwoButtonsChangeToOneExample.swift */; };
6D6E866F2C539CDE00EDB6F4 /* InPlaceLoadingFlexibleButtonExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6E866E2C539CDE00EDB6F4 /* InPlaceLoadingFlexibleButtonExample.swift */; };
6D6E86712C53A0D500EDB6F4 /* CardViewWithTwoButtonsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6E86702C53A0D500EDB6F4 /* CardViewWithTwoButtonsExample.swift */; };
6DE3F6A82D0FB9C600A4DAB6 /* ActionItemsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DE3F6A62D0FB9C600A4DAB6 /* ActionItemsExample.swift */; };
6DEC31F42C463ED50084DD20 /* FioriButtonTestsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEC31F32C463ED50084DD20 /* FioriButtonTestsExample.swift */; };
6DEC31F82C47B7850084DD20 /* FioriButtonStyleToggleExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEC31F72C47B7850084DD20 /* FioriButtonStyleToggleExample.swift */; };
6DEC31FA2C48B35D0084DD20 /* FioriButtonCustomButtonExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEC31F92C48B35D0084DD20 /* FioriButtonCustomButtonExample.swift */; };
Expand Down Expand Up @@ -271,6 +272,7 @@
6D6E866C2C53969A00EDB6F4 /* CardTwoButtonsChangeToOneExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTwoButtonsChangeToOneExample.swift; sourceTree = "<group>"; };
6D6E866E2C539CDE00EDB6F4 /* InPlaceLoadingFlexibleButtonExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPlaceLoadingFlexibleButtonExample.swift; sourceTree = "<group>"; };
6D6E86702C53A0D500EDB6F4 /* CardViewWithTwoButtonsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardViewWithTwoButtonsExample.swift; sourceTree = "<group>"; };
6DE3F6A62D0FB9C600A4DAB6 /* ActionItemsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionItemsExample.swift; sourceTree = "<group>"; };
6DEC31F32C463ED50084DD20 /* FioriButtonTestsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FioriButtonTestsExample.swift; sourceTree = "<group>"; };
6DEC31F72C47B7850084DD20 /* FioriButtonStyleToggleExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FioriButtonStyleToggleExample.swift; sourceTree = "<group>"; };
6DEC31F92C48B35D0084DD20 /* FioriButtonCustomButtonExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FioriButtonCustomButtonExample.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -583,6 +585,14 @@
path = ActivityItem;
sourceTree = "<group>";
};
6DE3F6A72D0FB9C600A4DAB6 /* ActionItems */ = {
isa = PBXGroup;
children = (
6DE3F6A62D0FB9C600A4DAB6 /* ActionItemsExample.swift */,
);
path = ActionItems;
sourceTree = "<group>";
};
8732C2C32C35092D002110E9 /* Timeline */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -676,6 +686,7 @@
8A5579C824C1293C0098003A /* FioriSwiftUICore */ = {
isa = PBXGroup;
children = (
6DE3F6A72D0FB9C600A4DAB6 /* ActionItems */,
6D66D7F02D02FC7B00F7A97D /* ActivityItem */,
87F14B192CD86F65004A69A0 /* DocumentScannerView */,
3CD71F272CDB625000B037EB /* CheckoutIndicator */,
Expand Down Expand Up @@ -1175,6 +1186,7 @@
3CC870962CB6F4F30081909C /* ToastMessageExample.swift in Sources */,
8A5579CC24C1293C0098003A /* SettingsColorForCategory.swift in Sources */,
6D6E866B2C5238A000EDB6F4 /* MultiLoadingButtonStatusChangeExample.swift in Sources */,
6DE3F6A82D0FB9C600A4DAB6 /* ActionItemsExample.swift in Sources */,
C18868D12B32535100F865F7 /* SearchFontAndColor.swift in Sources */,
9D0B26092B9BA5C0004278A5 /* KeyValueFormViewExample.swift in Sources */,
8732C2C52C350957002110E9 /* TimelineExample.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import FioriSwiftUICore
import SwiftUI

struct ActionItemsExample: View {
var body: some View {
List {
Section {
ActionItems(actionItems: [
.init(type: .phone, didSelectActivityItem: {
print("click phone")
})
])
ActionItems(actionItems: [
.init(type: .phone, didSelectActivityItem: {
print("click phone")
}),
.init(type: .email, didSelectActivityItem: {
print("click email")
})
])
ActionItems(actionItems: [
.init(type: .phone, didSelectActivityItem: {
print("click phone")
}),
.init(type: .email, didSelectActivityItem: {
print("click email")
}),
.init(type: .message, didSelectActivityItem: {
print("click message")
})
])
ActionItems(actionItems: [
.init(type: .phone, didSelectActivityItem: {
print("click phone")
}),
.init(type: .email, didSelectActivityItem: {
print("click email")
}),
.init(type: .message, didSelectActivityItem: {
print("click message")
}),
.init(type: .videoCall, didSelectActivityItem: {
print("click videoCall")
})
])
ActionItems(actionItems: [
.init(type: .phone, didSelectActivityItem: {
print("click phone")
}),
.init(type: .email, didSelectActivityItem: {
print("click email")
}),
.init(type: .message, didSelectActivityItem: {
print("click message")
}),
.init(type: .videoCall, didSelectActivityItem: {
print("click videoCall")
}),
.init(type: .detail, didSelectActivityItem: {
print("click detail")
})
])
.actionItemsStyle { conf in
conf.actionItems
.font(.fiori(forTextStyle: .headline).weight(.bold))
.foregroundColor(.red)
}
} header: {
Text("Usual Type")
.textCase(.none)
}

Section {
ActionItems(actionItems: [
.init(type: .custom(Image(systemName: "person")), didSelectActivityItem: {
print("custom person")
}),
.init(type: .custom(Image(systemName: "heart")), didSelectActivityItem: {
print("custom heart")
}),
.init(type: .custom(Image(systemName: "clock")), didSelectActivityItem: {
print("custom clock")
})
])
} header: {
Text("Custom Type")
.textCase(.none)
}

Section {
ActionItems {
Button {
print("click person")
} label: {
Image(systemName: "person")
.font(.fiori(forTextStyle: .headline).weight(.heavy))
.imageScale(.large)
.foregroundColor(.red)
.frame(width: 44, height: 44)
}
.buttonStyle(BorderlessButtonStyle())

Button {
print("click heart")
} label: {
Image(systemName: "heart")
.font(.fiori(forTextStyle: .headline).weight(.heavy))
.imageScale(.large)
.foregroundColor(.yellow)
.frame(width: 44, height: 44)
}
.buttonStyle(BorderlessButtonStyle())

Button {
print("click info.circle")
} label: {
Image(systemName: "info.circle")
.font(.fiori(forTextStyle: .headline).weight(.heavy))
.imageScale(.large)
.foregroundColor(.blue)
.frame(width: 44, height: 44)
}
.buttonStyle(BorderlessButtonStyle())

Button {
print("click book")
} label: {
Image(systemName: "book")
.font(.fiori(forTextStyle: .headline).weight(.heavy))
.imageScale(.large)
.foregroundColor(.green)
.frame(width: 44, height: 44)
}
.buttonStyle(BorderlessButtonStyle())
}
} header: {
Text("Custom items")
}
}
.navigationTitle("ActionItemsExample")
}
}

#Preview {
ActionItemsExample()
}
6 changes: 6 additions & 0 deletions Apps/Examples/Examples/FioriSwiftUICore/CoreContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ struct CoreContentView: View {
Text("ActivityItem")
}

NavigationLink {
ActionItemsExample()
} label: {
Text("ActionItems")
}

NavigationLink(
destination: AvatarStackExample(),
label: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@ public struct ActivityItemDataType: Identifiable {
public private(set) var id = UUID()

public private(set) var icon: Image

public private(set) var didSelectActivityItem: (() -> Void)?

public init(type: ActivityItemType, data: String? = nil) {
public init(type: ActivityItemType, data: String? = nil, didSelectActivityItem: (() -> Void)? = nil) {
self.icon = type.icon
self.type = type
self.data = data
self.didSelectActivityItem = didSelectActivityItem
}
}
111 changes: 111 additions & 0 deletions Sources/FioriSwiftUICore/Views/ActionItems/ActionItemsBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import SwiftUI

/// :nodoc:
public protocol ActionItemsList: View, _ViewEmptyChecking {
associatedtype V: View
var count: Int { get }
func view(at index: Int) -> V
}

struct ActionItemsListStack: ActionItemsList {
let actionItems: [any View]

init(_ actionItemsData: [ActivityItemDataType]) {
var temp: [any View] = []
for element in actionItemsData {
let item = Button {
element.didSelectActivityItem?()
} label: {
element.icon
.imageScale(.large)
.frame(width: 44, height: 44, alignment: .center)
}
.buttonStyle(BorderlessButtonStyle())
.typeErased

temp.append(item.typeErased)
}

self.actionItems = temp
}

init(actionItems: [any View]) {
self.actionItems = actionItems
}

public var count: Int {
self.actionItems.count
}

var isEmpty: Bool {
self.actionItems.isEmpty
}

public func view(at index: Int) -> some View {
self.actionItems[index].typeErased
}

var body: some View {
HStack(spacing: 0) {
ForEach(0 ..< self.actionItems.count, id: \.self) { index in
self.actionItems[index].typeErased
}
}
}
}

/// A custom parameter attribute that constructs views from closures.
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@resultBuilder
public enum ActionItemsBuilder {
/// :nodoc:
public static func buildBlock(_ components: any View...) -> some ActionItemsList {
let flatAvatars = components.flatMap { component -> [any View] in
if let c = component as? ActionItemsListStack {
return c.actionItems
} else {
return [component]
}
}
return ActionItemsListStack(actionItems: flatAvatars)
}

/// :nodoc:
public static func buildBlock() -> EmptyView {
EmptyView()
}

/// :nodoc:
public static func buildExpression(_ expression: some View) -> some View {
expression
}

/// :nodoc:
public static func buildExpression<Data: RandomAccessCollection>(
_ expression: ForEach<Data, Data.Element, some View>
) -> some ActionItemsList {
ActionItemsListStack(actionItems: expression.data.map { item in
expression.content(item)
})
}

/// :nodoc:
public static func buildEither(first: any View) -> some ActionItemsList {
ActionItemsListStack(actionItems: [first])
}

/// :nodoc:
public static func buildEither(second: any View) -> some ActionItemsList {
ActionItemsListStack(actionItems: [second])
}

/// :nodoc:
public static func buildOptional(_ component: (any View)?) -> some ActionItemsList {
ActionItemsListStack(actionItems: component.map { [$0] } ?? [])
}

/// :nodoc:
public static func buildArray(_ components: [any View]) -> some ActionItemsList {
ActionItemsListStack(actionItems: components)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,20 @@ protocol _ProgressComponent {
var progress: ProgressView<EmptyView, EmptyView> { get }
}

/// `ActionItems` provides a view that shows several items with action.
///
/// ## Usage
/// ```swift
/// ActionItems(actionItems: [.init(type: .phone), .init(type: .email), .init(type: .message), .init(type: .videoCall), .init(type: .detail)]) { dataType in
/// print("\(dataType)")
/// }
/// ```
// sourcery: BaseComponent
protocol _ActionItemsComponent {
// sourcery: resultBuilder.name = @ActionItemsBuilder, resultBuilder.backingComponent = ActionItemsListStack
var actionItems: [ActivityItemDataType] { get }
}

// sourcery: BaseComponent
protocol _LowerThumbComponent {
// sourcery: @ViewBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ protocol _ProgressIndicatorComponent: _ProgressIndicatorProtocol {}
// sourcery: CompositeComponent
protocol _ProcessingIndicatorComponent: _OptionalTitleComponent {}

/// `ActivityItem` is a View that provides a customizable activity item with icon and subtitle.
/// `ActivityItem` provides a customizable activity item with an icon and a subtitle.
///
/// ## Usage
/// ```swift
Expand Down
22 changes: 22 additions & 0 deletions Sources/FioriSwiftUICore/_FioriStyles/ActionItemsStyle.fiori.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import FioriThemeManager
import Foundation
import SwiftUI

// Base Layout style
public struct ActionItemsBaseStyle: ActionItemsStyle {
@ViewBuilder
public func makeBody(_ configuration: ActionItemsConfiguration) -> some View {
// Add default layout here
configuration.actionItems
}
}

// Default fiori styles
public struct ActionItemsFioriStyle: ActionItemsStyle {
@ViewBuilder
public func makeBody(_ configuration: ActionItemsConfiguration) -> some View {
ActionItems(configuration)
.font(.fiori(forTextStyle: .body).weight(.light))
.foregroundColor(.preferredColor(.tintColor))
}
}
Loading

0 comments on commit 33d3788

Please sign in to comment.