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

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

Merged
merged 3 commits into from
Sep 27, 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
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
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
}
}
Loading