Skip to content

Commit

Permalink
feat: 🎸 [JIRA:IOSSDKBUG-347] FilterFeedbackBar picker enhance (#807)
Browse files Browse the repository at this point in the history
* feat: 🎸 [JIRA:IOSSDKBUG-347] FilterFeedbackBar picker enhance

FilterFeedbackBar picker sheet enhancement, custom item layout

* feat: 🎸 [JIRA:IOSSDKBUG-347] FilterFeedbackBar picker enhance

---------

Co-authored-by: Bill Zhou <bill.zhou01@sap.com>
  • Loading branch information
restaurantt and billzhou0223 authored Sep 27, 2024
1 parent 01c1245 commit 14bb1aa
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct SortFilterExample: View {
[
.switch(item: .init(name: "Favorite", value: true, icon: "heart.fill"), showsOnFilterFeedbackBar: true),
.switch(item: .init(name: "Tagged", value: nil, icon: "tag"), showsOnFilterFeedbackBar: false),
.picker(item: .init(name: "JIRA Status", value: [0], valueOptions: ["Received", "Started", "Hold", "Transfer", "Completed", "Pending Review", "Accepted", "Rejected"], allowsMultipleSelection: true, allowsEmptySelection: true, icon: "clock"), showsOnFilterFeedbackBar: true)
.picker(item: .init(name: "JIRA Status", value: [0], valueOptions: ["Received", "Started", "Hold", "Transfer", "Completed", "Pending Review Pending Pending Pending Pending Pending", "Accepted Medium", "Rejected"], allowsMultipleSelection: true, allowsEmptySelection: true, icon: "clock", itemLayout: .flexible), showsOnFilterFeedbackBar: true)
],
[
.picker(item: .init(name: "Priority", value: [0], valueOptions: ["High", "Medium", "Low"], allowsMultipleSelection: true, allowsEmptySelection: true, icon: "filemenu.and.cursorarrow"), showsOnFilterFeedbackBar: true),
Expand Down
4 changes: 3 additions & 1 deletion Sources/FioriSwiftUICore/DataTypes/SortFilter+DataType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,9 @@ public extension SortFilterItem {
public let allowsMultipleSelection: Bool
public let allowsEmptySelection: Bool
public let icon: String?
public var itemLayout: OptionListPickerItemLayoutType = .fixed

public init(id: String = UUID().uuidString, name: String, value: [Int], valueOptions: [String], allowsMultipleSelection: Bool, allowsEmptySelection: Bool, icon: String? = nil) {
public init(id: String = UUID().uuidString, name: String, value: [Int], valueOptions: [String], allowsMultipleSelection: Bool, allowsEmptySelection: Bool, icon: String? = nil, itemLayout: OptionListPickerItemLayoutType = .fixed) {
self.id = id
self.name = name
self.value = value
Expand All @@ -321,6 +322,7 @@ public extension SortFilterItem {
self.allowsMultipleSelection = allowsMultipleSelection
self.allowsEmptySelection = allowsEmptySelection
self.icon = icon
self.itemLayout = itemLayout
}

mutating func onTap(option: String) {
Expand Down
4 changes: 4 additions & 0 deletions Sources/FioriSwiftUICore/Models/ModelDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,10 @@ public protocol FilterFeedbackBarButtonModel: LeftIconComponent, TitleComponent
// sourcery: add_env_props = "filterFeedbackBarStyle"
// sourcery: generated_component_not_configurable
public protocol OptionListPickerItemModel: OptionListPickerComponent {
// sourcery: default.value = .fixed
// sourcery: no_view
var itemLayout: OptionListPickerItemLayoutType { get set }

// sourcery: default.value = nil
// sourcery: no_view
var onTap: ((_ index: Int) -> Void)? { get }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public struct DefaultOptionListPickerStyle: OptionListPickerStyle {
let minTouchHeight: CGFloat

/// :nodoc:
public init(font: Font = .system(.body), foregroundColorSelected: Color = .preferredColor(.tintColor), foregroundColorUnselected: Color = .preferredColor(.tertiaryLabel), fillColorSelected: Color = .preferredColor(.primaryFill), fillColorUnselected: Color = .preferredColor(.secondaryFill), strokeColorSelected: Color = .preferredColor(.tintColor), strokeColorUnselected: Color = .preferredColor(.separator), cornerRadius: CGFloat = 16, spacing: CGFloat = 6, borderWidth: CGFloat = 1, minHeight: CGFloat = 44, minTouchHeight: CGFloat = 56) {
public init(font: Font = .system(.body), foregroundColorSelected: Color = .preferredColor(.tintColor), foregroundColorUnselected: Color = .preferredColor(.tertiaryLabel), fillColorSelected: Color = .preferredColor(.secondaryGroupedBackground), fillColorUnselected: Color = .preferredColor(.tertiaryFill), strokeColorSelected: Color = .preferredColor(.tintColor), strokeColorUnselected: Color = .preferredColor(.separator), cornerRadius: CGFloat = 16, spacing: CGFloat = 4, borderWidth: CGFloat = 1, minHeight: CGFloat = 44, minTouchHeight: CGFloat = 50) {
self.font = font
self.foregroundColorSelected = foregroundColorSelected
self.foregroundColorUnselected = foregroundColorUnselected
Expand All @@ -89,7 +89,9 @@ public struct DefaultOptionListPickerStyle: OptionListPickerStyle {
HStack(spacing: self.spacing) {
configuration.leftIcon
configuration.title
.lineLimit(1)
}
.padding([.leading, .trailing], configuration.isSelected ? 9 : 20)
.font(self.font)
.foregroundColor(configuration.isSelected ? self.foregroundColorSelected : self.foregroundColorUnselected)
.frame(maxWidth: .infinity, minHeight: self.minHeight)
Expand Down
77 changes: 76 additions & 1 deletion Sources/FioriSwiftUICore/Views/OptionListPickerItem+View.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import SwiftUI

/// Available OptionListPickerItem layout types. Use this enum to define item layout type to present.
public enum OptionListPickerItemLayoutType {
/// Fixed width
case fixed
/// Column width will be flexible
case flexible
}

extension OptionListPickerItem: View {
public var body: some View {
if _itemLayout == .flexible {
self.generateFlexibleContent()
} else {
self.generateFixedContent()
}
}

private func generateFixedContent() -> some View {
Grid(horizontalSpacing: 16) {
ForEach(0 ..< Int(ceil(Double(_valueOptions.count) / 2.0)), id: \.self) { rowIndex in
GridRow {
Expand All @@ -27,6 +43,21 @@ extension OptionListPickerItem: View {
}
}
}

private func generateFlexibleContent() -> some View {
OptionListPickerCustomLayout {
ForEach(0 ..< _valueOptions.count, id: \.self) { optionIndex in
FilterFeedbackBarButton(
leftIcon: _value.wrappedValue.contains(optionIndex) ? Image(systemName: "checkmark") : nil,
title: _valueOptions[optionIndex],
isSelected: _value.wrappedValue.contains(optionIndex)
)
.onTapGesture {
_onTap?(optionIndex)
}
}
}
}
}

/*
Expand All @@ -43,7 +74,7 @@ extension OptionListPickerItem: View {
#Preview {
VStack {
Spacer()
OptionListPickerItem(value: Binding<[Int]>(get: { [0, 1, 2] }, set: { print($0) }), valueOptions: ["Received", "Started", "Hold", "Transfer", "Completed", "Pending Review", "Accepted", "Rejected"], hint: nil)
OptionListPickerItem(value: Binding<[Int]>(get: { [0, 1, 2] }, set: { print($0) }), valueOptions: ["Received", "Started", "Hold", "Transfer", "Completed", "Pending Review review", "Accepted", "Rejected"], hint: nil)
.frame(width: 375)
Spacer()
OptionListPickerItem(value: Binding<[Int]>(get: { [0, 1, 2] }, set: { print($0) }), valueOptions: ["Received", "Started", "Hold", "Transfer", "Completed", "Pending Review", "Accepted", "Rejected"], hint: nil)
Expand All @@ -52,3 +83,47 @@ extension OptionListPickerItem: View {
Spacer()
}
}

struct OptionListPickerCustomLayout: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
guard let containerWidth = proposal.width else {
return .zero
}
var containerHeight = 0.0
var currentRowX = 16.0
for index in 0 ..< subviews.count {
let subview = subviews[index]
let subviewSize = subview.sizeThatFits(.unspecified)
let subviewWidth = min(subviewSize.width, containerWidth)
if index == 0 {
containerHeight += subviewSize.height
}
if currentRowX + subviewWidth + 16.0 > containerWidth {
containerHeight += subviewSize.height
containerHeight += 6
currentRowX = 16.0
}
currentRowX += subviewWidth + 6.0
}
return CGSize(width: containerWidth, height: containerHeight)
}

func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
guard let containerWidth = proposal.width else { return }
var currentY: CGFloat = bounds.minY
var currentRowX = 16.0
for subview in subviews {
let subviewSize = subview.sizeThatFits(.unspecified)
let subviewWidth = min(subviewSize.width, containerWidth)
if currentRowX + subviewWidth + 16.0 > containerWidth {
currentY += subviewSize.height
currentY += 6
currentRowX = 16.0
subview.place(at: CGPoint(x: currentRowX, y: currentY), proposal: ProposedViewSize(width: subviewWidth, height: subviewSize.height))
} else {
subview.place(at: CGPoint(x: currentRowX, y: currentY), proposal: ProposedViewSize(width: subviewWidth, height: subviewSize.height))
}
currentRowX += subviewWidth + 6.0
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ struct PickerMenuItem: View {
})
.buttonStyle(ApplyButtonStyle())
} components: {
OptionListPickerItem(value: self.$item.workingValue, valueOptions: self.item.valueOptions, hint: nil) { index in
OptionListPickerItem(value: self.$item.workingValue, valueOptions: self.item.valueOptions, hint: nil, itemLayout: self.item.itemLayout) { index in
self.item.onTap(option: self.item.valueOptions[index])
}
.padding([.leading, .trailing], UIDevice.current.userInterfaceIdiom == .pad ? 13 : 16)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ public struct OptionListPickerItem {
var _value: Binding<[Int]>
var _valueOptions: [String]
var _hint: String? = nil
var _itemLayout: OptionListPickerItemLayoutType
var _onTap: ((_ index: Int) -> Void)? = nil

public init(model: OptionListPickerItemModel) {
self.init(value: Binding<[Int]>(get: { model.value }, set: { model.value = $0 }), valueOptions: model.valueOptions, hint: model.hint, onTap: model.onTap)
self.init(value: Binding<[Int]>(get: { model.value }, set: { model.value = $0 }), valueOptions: model.valueOptions, hint: model.hint, itemLayout: model.itemLayout, onTap: model.onTap)
}

public init(value: Binding<[Int]>, valueOptions: [String] = [], hint: String? = nil, onTap: ((_ index: Int) -> Void)? = nil) {
public init(value: Binding<[Int]>, valueOptions: [String] = [], hint: String? = nil, itemLayout: OptionListPickerItemLayoutType = .fixed, onTap: ((_ index: Int) -> Void)? = nil) {
self._value = value
self._valueOptions = valueOptions
self._hint = hint
self._itemLayout = itemLayout
self._onTap = onTap
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import SwiftUI

public extension OptionListPickerItemModel {
var onTap: ((_ index: Int) -> Void)? {
var itemLayout: OptionListPickerItemLayoutType {
return .fixed
}

var onTap: ((_ index: Int) -> Void)? {
return nil
}
}

0 comments on commit 14bb1aa

Please sign in to comment.