Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 💡 step progress indicator #944

Merged
merged 3 commits into from
Dec 23, 2024
Merged
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
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 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

// 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 }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)


// 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 {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Docs Violation: public declarations should be documented. (missing_docs)


// 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
Loading