Skip to content

Commit

Permalink
refactor: 💡 step progress indicator (#944)
Browse files Browse the repository at this point in the history
* refactor: 💡 step progress indicator

* docs: ✏️ update

---------

Co-authored-by: dyongxu <61523257+dyongxu@users.noreply.github.com>
  • Loading branch information
xiaoyu0722 and dyongxu authored Dec 23, 2024
1 parent 46e9a91 commit cf92494
Show file tree
Hide file tree
Showing 40 changed files with 2,031 additions and 208 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import FioriSwiftUICore
import SwiftUI

class SPIExampleModel: StepProgressIndicatorModel, ObservableObject {
class SPIExampleModel: _StepProgressIndicatorModel, ObservableObject {
struct AllActionModel: _ActionModel {
let actionText: String? = "All Steps"

Expand All @@ -11,7 +11,7 @@ class SPIExampleModel: StepProgressIndicatorModel, ObservableObject {
@Published var selection: String = "b"
var title: String? = "SPI Title: b"

var steps: [SingleStepModel] = ["a", "b", "c", "d", "e", "f"]
var steps: [_SingleStepModel] = ["a", "b", "c", "d", "e", "f"]
.map {
SingelExampleModel(id: $0,
title: "title: \($0)",
Expand All @@ -23,16 +23,16 @@ class SPIExampleModel: StepProgressIndicatorModel, ObservableObject {
}
}

class SingelExampleModel: SingleStepModel {
class SingelExampleModel: _SingleStepModel {
var id: String = ""
var title: String? = ""
var node: TextOrIcon
var substeps: [SingleStepModel] = []
var substeps: [_SingleStepModel] = []

init(id: String = UUID().uuidString,
title: String? = nil,
node: TextOrIcon,
substeps: [SingleStepModel] = [])
substeps: [_SingleStepModel] = [])
{
self.id = id
self.title = title
Expand All @@ -47,7 +47,7 @@ struct SPIModelExample: View {
var body: some View {
VStack(alignment: .leading) {
Text("Initialized by Model").bold()
StepProgressIndicator(model: self.model)
_StepProgressIndicator(model: self.model)
.stepStyle { id in
CustomModelStyleExample(isLast: id == "f")
}
Expand Down

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions Sources/FioriSwiftUICore/Models/ModelDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ public protocol KPIHeaderItemModel {}
// sourcery: virtualPropStepState = "var state: StepProgressIndicatorState?"
// sourcery: virtualPropIsLastStep = "var isLastStep: Bool = false"
// sourcery: generated_component_composite
public protocol SingleStepModel {
public protocol _SingleStepModel {
// sourcery: default.value = UUID().uuidString
// sourcery: no_view
var id: String { get set }
Expand All @@ -446,17 +446,20 @@ public protocol SingleStepModel {
// sourcery: backingComponent=_StepsContainer
// sourcery: customFunctionBuilder=IndexedViewBuilder
// sourcery: genericParameter.type=IndexedViewContainer
var substeps: [SingleStepModel] { get set }
var substeps: [_SingleStepModel] { get set }
}

@available(*, unavailable, renamed: "_SingleStepModel", message: "Will be removed in the future release. Please use SingleStep instead.")
public protocol SingleStepModel {}

// sourcery: add_env_props = "presentationMode"
// sourcery: virtualPropAxis = "var axis: Axis = .horizontal"
// sourcery: virtualPropStepsData = "var stepItems: [StepItem] = []"
// sourcery: virtualPropIsPresented = "@State var isPresented: Bool = false"
// sourcery: virtualPropStepFrames = "@State var stepFrames: [String: CGRect] = [:]"
// sourcery: virtualPropScrollBounds = "@State var scrollBounds: CGRect = .zero"
// sourcery: generated_component_composite
public protocol StepProgressIndicatorModel: AnyObject {
public protocol _StepProgressIndicatorModel: AnyObject {
// sourcery: bindingProperty
// sourcery: no_view
var selection: String { get set }
Expand All @@ -471,13 +474,16 @@ public protocol StepProgressIndicatorModel: AnyObject {
// sourcery: backingComponent=_StepsContainer
// sourcery: customFunctionBuilder=IndexedViewBuilder
// sourcery: genericParameter.type=IndexedViewContainer
var steps: [SingleStepModel] { get }
var steps: [_SingleStepModel] { get }

// sourcery: genericParameter.name = CancelActionView
// sourcery: default.value = _CancelActionDefault()
var cancelAction: _ActionModel? { get }
}

@available(*, unavailable, renamed: "_StepProgressIndicatorModel", message: "Will be removed in the future release. Please use StepProgressIndicator instead.")
public protocol StepProgressIndicatorModel {}

// sourcery: generated_component_composite
public protocol FilterFeedbackBarModel: AnyObject {
// sourcery: bindingProperty
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import SwiftUI

struct StepsStack: View {
var steps: [StepItem]
var selection: Binding<String>? = nil
/// :nodoc:
init(_ steps: [StepItem], selection: Binding<String>? = nil) {
self.steps = steps
self.selection = selection
}

var body: some View {
ForEach(0 ..< self.count, id: \.self) { index in
self.view(at: index)
}
}
}

extension StepsStack: IndexedViewContainer {
/// :nodoc:
public var count: Int {
self.steps.count
}

/// :nodoc:
@ViewBuilder public func view(at index: Int) -> some View {
if index < self.count {
_DefaultSteps(stepItems: self.steps, selection: self.selection ?? .constant("")).view(at: index)
} else {
EmptyView()
}
}

@ViewBuilder func titleView(for title: String?) -> some View {
if let title {
Text(title)
} else {
EmptyView()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ struct DefaultSingleStep: View {
func singleStep() -> some View {
if self.stepItem.state.isSupported {
let isSelected = self.stepItem.id == self.selection
SingleStep(id: self.stepItem.id) {
_SingleStep(id: self.stepItem.id) {
if let title = stepItem.title {
Text(title)
} else {
Expand Down Expand Up @@ -130,7 +130,7 @@ struct DefaultSingleStep: View {
func singleSubstep() -> some View {
if self.stepItem.state.isSupported {
let isSelected = self.stepItem.id == self.selection
SingleStep(id: self.stepItem.id) {
_SingleStep(id: self.stepItem.id) {
if let title = stepItem.title {
Text(title)
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SwiftUI

extension Fiori {
enum SingleStep {
enum _SingleStep {
typealias Title = EmptyModifier
typealias TitleCumulative = EmptyModifier
typealias Node = EmptyModifier
Expand Down Expand Up @@ -37,7 +37,7 @@ public struct _StepNode: View {
}
}

public extension SingleStep where Title == _ConditionalContent<Text, EmptyView>,
public extension _SingleStep where Title == _ConditionalContent<Text, EmptyView>,
Node == _StepNode,
Substeps == _StepItemsContainer
{
Expand All @@ -56,7 +56,7 @@ public extension SingleStep where Title == _ConditionalContent<Text, EmptyView>,
}
}

public extension SingleStep where Substeps == EmptyView {
public extension _SingleStep where Substeps == EmptyView {
/// Convenience initialization for empty sub-steps.
/// - Parameters:
/// - id: String value for step id.
Expand All @@ -73,7 +73,7 @@ public extension SingleStep where Substeps == EmptyView {
}
}

public extension SingleStep where Title == EmptyView, Substeps == EmptyView {
public extension _SingleStep where Title == EmptyView, Substeps == EmptyView {
/// Convenience initialization for empty title and sub-steps.
/// - Parameters:
/// - id: String value for step id.
Expand All @@ -85,7 +85,7 @@ public extension SingleStep where Title == EmptyView, Substeps == EmptyView {
}
}

extension SingleStep: View {
extension _SingleStep: View {
var stepsSpacing: CGFloat {
2
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import FioriThemeManager
import SwiftUI

extension Fiori {
enum StepProgressIndicator {
enum _StepProgressIndicator {
typealias Title = EmptyModifier
typealias TitleCumulative = EmptyModifier
typealias Action = EmptyModifier
Expand All @@ -18,7 +18,7 @@ extension Fiori {
}
}

extension StepProgressIndicator: View {
extension _StepProgressIndicator: View {
var stepsCount: Int {
steps.count
}
Expand Down Expand Up @@ -120,13 +120,13 @@ extension StepProgressIndicator: View {
}

/// :nodoc:
public extension StepProgressIndicator where Steps == _DefaultSteps, CancelActionView == _Action {
/// Convenience initialization for default step progress indicator.
public extension _StepProgressIndicator where Steps == _DefaultSteps, CancelActionView == _Action {
/// Convenience initializer for default step progress indicator.
/// - Parameters:
/// - selection: A binding string for selected step id.
/// - stepItems: An array of `StepItem` for default steps generation.
/// - title: Title for current step displayed on steps top-left .
/// - action: Action for steps displayed on steps top-right that will show a vertical steps.
/// - title: Title for current step displayed on the top leading side of the step progress indicator.
/// - action: Action for steps displayed on the top trailing side of the step progress indicator. It will show vertical steps.
init(selection: Binding<String>,
stepItems: [StepItem],
@ViewBuilder title: @escaping () -> Title,
Expand All @@ -142,11 +142,11 @@ public extension StepProgressIndicator where Steps == _DefaultSteps, CancelActio
selection: selection)
}

/// Convenience initialization for default step progress indicator.
/// Convenience initializer for default step progress indicator.
/// - Parameters:
/// - selection: A binding string for selected step id.
/// - stepItems: An array of `StepItem` for default steps generation.
/// - title: Title for current step displayed on steps top-left .
/// - title: Title for current step displayed on the top leading side of the step progress indicator.
init(selection: Binding<String>,
stepItems: [StepItem],
@ViewBuilder title: @escaping () -> Title) where ActionView == EmptyView
Expand All @@ -157,11 +157,11 @@ public extension StepProgressIndicator where Steps == _DefaultSteps, CancelActio
action: { EmptyView() })
}

/// Convenience initialization for default step progress indicator.
/// Convenience initializer for default step progress indicator.
/// - Parameters:
/// - selection: A binding string for selected step id.
/// - stepItems: An array of `StepItem` for default steps generation.
/// - action: Action for steps displayed on steps top-right that will show a vertical steps.
/// - action: Action for steps displayed on the top trailing side of the step progress indicator. It will show vertical steps.
init(selection: Binding<String>,
stepItems: [StepItem],
@ViewBuilder action: @escaping () -> ActionView) where Title == EmptyView
Expand All @@ -172,7 +172,7 @@ public extension StepProgressIndicator where Steps == _DefaultSteps, CancelActio
action: action)
}

/// Convenience initialization for default step progress indicator.
/// Convenience initializer for default step progress indicator.
/// - Parameters:
/// - selection: A binding string for selected step id.
/// - stepItems: An array of `StepItem` for default steps generation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import SwiftUI

/// Not used by developers.
public struct _StepsContainer {
var steps: [SingleStepModel]
var steps: [_SingleStepModel]
/// :nodoc:
public init(steps: [SingleStepModel]) {
public init(steps: [_SingleStepModel]) {
self.steps = steps
}

/// :nodoc:
public init(substeps: [SingleStepModel]) {
public init(substeps: [_SingleStepModel]) {
self.steps = substeps
}
}
Expand All @@ -28,10 +28,10 @@ extension _StepsContainer: IndexedViewContainer {
let title = self.steps[index].title
let node = self.steps[index].node
let substeps = self.steps[index].substeps
SingleStep(id: id,
title: title,
node: node,
substeps: substeps)
_SingleStep(id: id,
title: title,
node: node,
substeps: substeps)
} else {
EmptyView()
}
Expand All @@ -56,9 +56,18 @@ extension _StepItemsContainer: IndexedViewContainer {
/// :nodoc:
@ViewBuilder public func view(at index: Int) -> some View {
if index < self.count {
SingleStep(item: self.steps[index])
_SingleStep(item: self.steps[index])
} else {
EmptyView()
}
}
}

extension _StepItemsContainer: View {
/// :nodoc:
public var body: some View {
ForEach(0 ..< self.count, id: \.self) { index in
self.view(at: index)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,16 @@ protocol _ProgressComponent {
// sourcery: defaultValue = "ProgressView()"
var progress: ProgressView<EmptyView, EmptyView> { get }
}

// sourcery: BaseComponent
protocol _NodeComponent {
// sourcery: resultBuilder.name = @ViewBuilder, resultBuilder.backingComponent = TextOrIconView
var node: TextOrIcon? { get }
}

// sourcery: BaseComponent
protocol _LineComponent {
// sourcery: defaultValue = "{ Rectangle() }"
@ViewBuilder
var line: (() -> any View)? { get }
}
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,42 @@ protocol _ActivityItemComponent: _IconComponent, _SubtitleComponent {
// sourcery: defaultValue = .vertical
var layout: ActivityItemLayout { get }
}

// sourcery: CompositeComponent
protocol _SingleStepComponent: _TitleComponent, _NodeComponent, _LineComponent {
// sourcery: default.value = UUID().uuidString
// sourcery: no_view
var id: String { get }

// sourcery: default.value = .normal
// sourcery: no_view
var state: StepProgressIndicatorState { get }

// sourcery: resultBuilder.backingComponent = StepsStack
// sourcery: resultBuilder.name = @IndexedViewBuilder
// sourcery: resultBuilder.returnType = any IndexedViewContainer
var substeps: [StepItem] { get }
}

/// `StepProgressIndicator` is a view supporting a list of `StepItem` in a horizontal stack. Also customized steps are also supported.
/// ## Usage
/// ```swift
/// @State var selection: String = "id"
/// var steps: [StepItem] = []
/// StepProgressIndicator(selection: self.$selection,
/// stepItems: self.steps)
/// Also indexed view builder is also supported.
/// StepProgressIndicator(title: <#T##() -> any View#>, action: <#T##() -> any View#>, cancelAction: <#T##() -> any View#>, selection: <#T##Binding<String>#>, steps: <#T##() -> any IndexedViewContainer#>)
/// ```
/// You can also update step style for different states, if you created `StepProgressIndicator` by `[StepItem]`.
/// `func stepStyle(_ style: @escaping ((_ id: String) -> (some StepStyle)?)) -> some View`
// sourcery: CompositeComponent
protocol _StepProgressIndicatorComponent: _TitleComponent, _ActionComponent, _CancelActionComponent {
// sourcery: @Binding
var selection: String { get }

// sourcery: resultBuilder.backingComponent = StepsStack
// sourcery: resultBuilder.name = @IndexedViewBuilder
// sourcery: resultBuilder.returnType = any IndexedViewContainer
var steps: [StepItem] { get }
}
Loading

0 comments on commit cf92494

Please sign in to comment.