Skip to content

Commit

Permalink
Merge branch 'main' into feat/HCPSDKFIORIUIKIT-2880
Browse files Browse the repository at this point in the history
  • Loading branch information
dyongxu authored Dec 6, 2024
2 parents 83e9216 + 99a88ab commit 2c6d5a4
Show file tree
Hide file tree
Showing 20 changed files with 628 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Foundation
import SwiftUI

struct LoadingIndicatorExample: View {
@State var isPresented1: Bool = true
@State var isPresented2: Bool = false
@State var isPresented3: Bool = true
@State var isPresented4: Bool = true
Expand All @@ -19,106 +18,135 @@ struct LoadingIndicatorExample: View {
}

var body: some View {
List {
Section("Default Loading Indicator") {
LoadingIndicator(title: "Loading...", isPresented: self.$isPresented1)
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
.padding()
}.headerProminence(.increased)
ScrollView {
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [.sectionHeaders]) {
Section {
LoadingIndicator(title: "Loading...", isPresented: .constant(true))
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
.padding()
} header: {
Text("Default Loading Indicator")
.padding(.leading, 20)
.fontWeight(.bold)
}.padding(.bottom, 10)

Section {
VStack {
Rectangle()
.fill(Color.preferredColor(.accentBackground10))
.frame(height: 150)
.overlay(alignment: .center, content: {
LoadingIndicator(title: {
Text(" delay/show/dismiss")
.font(.system(size: 12))
.rainbow()
}, progress: {
Progress()
}, duration: 3, isPresented: self.$isPresented2)
.indicatorPosition(.top)
.indicatorTint(Color.random)
})
Button("Show/Hide Customized Loading Indicator") {
self.isPresented2.toggle()
}.buttonStyle(.borderedProminent)
}
} header: {
Text("Based On The Use Of Overlay")
.padding(.leading, 20)
.fontWeight(.bold)
}.padding(.bottom, 10)

Section {
VStack(alignment: .leading) {
LoadingIndicator(title: "mini size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.mini)
LoadingIndicator(title: "small size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.small)
LoadingIndicator(title: "regular size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.regular)
LoadingIndicator(title: "large size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.large)
if #available(iOS 17.0, *) {
LoadingIndicator(title: "extraLarge size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.extraLarge)
} else {
// Fallback on earlier versions
}
}
.padding(.leading, 20)
} header: {
Text("Control Size")
.padding(.leading, 20)
.fontWeight(.bold)
}.padding(.bottom, 10)

Section("Based On The Use Of Overlay") {
VStack {
Rectangle()
.fill(Color.preferredColor(.accentBackground10))
.frame(height: 150)
.overlay(alignment: .center, content: {
Section {
VStack(alignment: .center) {
LoadingIndicator(title: "leading indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorTint(Color.random)
Spacer()
if #available(iOS 17.0, *) {
LoadingIndicator(title: {
Text(" delay/show/dismiss")
.font(.system(size: 12))
.rainbow()
}, duration: 3, isPresented: self.$isPresented2)
Text("top indicator")
.font(.system(size: 8))
.foregroundStyle(Color.red)
}, progress: {
Progress()
}, isPresented: self.$isPresented5)
.indicatorPosition(.top)
.indicatorTint(Color.random)
})
Button("Show/Hide Customized Loading Indicator") {
self.isPresented2.toggle()
}.buttonStyle(.borderedProminent)
}
}.headerProminence(.increased)
Section("Control Size") {
VStack(alignment: .leading) {
LoadingIndicator(title: "mini size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.mini)
LoadingIndicator(title: "small size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.small)
LoadingIndicator(title: "regular size indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorControlSize(.regular)
LoadingIndicator(title: "large size indicator", isPresented: self.$isPresented6)
.indicatorPosition(.leading)
.indicatorControlSize(.large)
if #available(iOS 17.0, *) {
LoadingIndicator(title: "extraLarge size indicator", isPresented: self.$isPresented6)
.indicatorPosition(.leading)
.indicatorControlSize(.extraLarge)
} else {
// Fallback on earlier versions
}
}
}.headerProminence(.increased)
Section("Position And Custom Text") {
VStack(alignment: .center) {
LoadingIndicator(title: "leading indicator", isPresented: self.$isPresented3)
.indicatorPosition(.leading)
.indicatorTint(Color.random)
Spacer()
if #available(iOS 17.0, *) {
} else {
// Fallback on earlier versions
}
Spacer()
LoadingIndicator(title: {
Text("top indicator")
.font(.system(size: 8))
.foregroundStyle(Color.red)
}, isPresented: self.$isPresented5)
.indicatorPosition(.top)
Text("trailing indicator")
.font(.largeTitle)
.foregroundStyle(Color.green)
}, progress: {
Progress()
}, isPresented: self.$isPresented4)
.indicatorPosition(.trailing)
.indicatorTint(Color.random)
Spacer()
LoadingIndicator(title: self.message, isPresented: self.$isPresented6)
.indicatorPosition(.bottom)
.indicatorTint(Color.random)
} else {
// Fallback on earlier versions
}
Spacer()
LoadingIndicator(title: {
Text("trailing indicator")
.font(.largeTitle)
.foregroundStyle(Color.green)
}, isPresented: self.$isPresented4)
.indicatorPosition(.trailing)
.indicatorTint(Color.random)
Spacer()
LoadingIndicator(title: self.message, isPresented: self.$isPresented6)
.indicatorPosition(.bottom)
.indicatorTint(Color.random)
}
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
.padding()
}
.headerProminence(.increased)

Section("Delay 10 Seconds Dismiss") {
VStack {
LoadingIndicator(title: "Loading...", duration: 10, isPresented: self.$isPresented7)
.indicatorTint(Color.random)
.indicatorControlSize(.large)
Button("Show") {
self.isPresented7 = true
}.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
.padding()
} header: {
Text("Position And Custom Text")
.padding(.leading, 20)
.fontWeight(.bold)
}.padding(.bottom, 10)

Section {
VStack {
LoadingIndicator(title: "Loading...", duration: 10, isPresented: self.$isPresented7)
.indicatorTint(Color.random)
.indicatorControlSize(.large)
Button("Show") {
self.isPresented7 = true
}.buttonStyle(.borderedProminent)
}
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
.padding()
} header: {
Text("Delay 10 Seconds Dismiss")
.padding(.leading, 20)
.fontWeight(.bold)
}
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
.padding()
}
.headerProminence(.increased)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/FioriSwiftUICore/Models/ModelDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,8 @@ public protocol FilterFeedbackBarButtonModel: LeftIconComponent, TitleComponent
// sourcery: add_env_props = "filterFeedbackBarStyle"
// sourcery: generated_component_not_configurable
// sourcery: virtualPropHeight = "@State var _height: CGFloat = 0"
// sourcery: virtualPropUpdateSearchListPickerHeight = "var updateSearchListPickerHeight: ((CGFloat) -> ())? = nil"
// sourcery: virtualPropBarItemFrame = "var barItemFrame: CGRect = .zero"
public protocol OptionListPickerItemModel: OptionListPickerComponent {
// sourcery: default.value = .fixed
// sourcery: no_view
Expand Down
65 changes: 47 additions & 18 deletions Sources/FioriSwiftUICore/Views/OptionListPickerItem+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ public enum OptionListPickerItemLayoutType {
case flexible
}

public extension OptionListPickerItem {
/// create a filter picker which is used in FilterFeedbackBarItem
/// - Parameters:
/// - value: Indexes for selected values.
/// - valueOptions: The data for constructing the list picker.
/// - hint: Hint message.
/// - itemLayout: Option item layout type.
/// - barItemFrame: The frame of the item in FilterFeedbackBar, which toggle to show this view.
/// - onTap: The closure when tap on item.
/// - updateSearchListPickerHeight: The closure to update the parent view.
init(value: Binding<[Int]>, valueOptions: [String] = [], hint: String? = nil, itemLayout: OptionListPickerItemLayoutType = .fixed, barItemFrame: CGRect = .zero, onTap: ((_ index: Int) -> Void)? = nil, updateSearchListPickerHeight: ((CGFloat) -> Void)? = nil) {
self.init(value: value, valueOptions: valueOptions, hint: hint, itemLayout: itemLayout, onTap: onTap)

self.barItemFrame = barItemFrame
self.updateSearchListPickerHeight = updateSearchListPickerHeight
}
}

extension OptionListPickerItem: View {
public var body: some View {
if _itemLayout == .flexible {
Expand Down Expand Up @@ -61,19 +79,11 @@ extension OptionListPickerItem: View {
GeometryReader { geometry in
Color.clear
.onAppear {
let popverHeight = Screen.bounds.size.height
let totalSpacing: CGFloat = (UIDevice.current.userInterfaceIdiom != .phone ? 8 : 16) * 2
let totalPadding: CGFloat = (UIDevice.current.userInterfaceIdiom != .phone ? 13 : 16) * 2
let safeAreaInset = self.getSafeAreaInsets()
let maxScrollViewHeight = popverHeight - totalSpacing - totalPadding - safeAreaInset.top - safeAreaInset.bottom - (UIDevice.current.userInterfaceIdiom != .phone ? 210 : 60)
self._height = min(geometry.size.height, maxScrollViewHeight)
self.updateSearchListPickerHeight?(self.calculateHeight(scrollViewContentHeight: geometry.size.height))
}
}
)
}
.ifApply(UIDevice.current.userInterfaceIdiom == .phone, content: { v in
v.frame(height: _height)
})
}

private func generateFlexibleContent() -> some View {
Expand All @@ -94,19 +104,14 @@ extension OptionListPickerItem: View {
GeometryReader { geometry in
Color.clear
.onAppear {
let popverHeight = Screen.bounds.size.height
let totalSpacing: CGFloat = (UIDevice.current.userInterfaceIdiom != .phone ? 8 : 16) * 2
let totalPadding: CGFloat = (UIDevice.current.userInterfaceIdiom != .phone ? 13 : 16) * 2
let safeAreaInset = self.getSafeAreaInsets()
let maxScrollViewHeight = popverHeight - totalSpacing - totalPadding - safeAreaInset.top - safeAreaInset.bottom - (UIDevice.current.userInterfaceIdiom != .phone ? 210 : 60)
self._height = min(geometry.size.height, maxScrollViewHeight)
self.updateSearchListPickerHeight?(self.calculateHeight(scrollViewContentHeight: geometry.size.height))
}
.onChange(of: geometry.size) { _ in
self.updateSearchListPickerHeight?(self.calculateHeight(scrollViewContentHeight: geometry.size.height))
}
}
)
}
.ifApply(UIDevice.current.userInterfaceIdiom == .phone, content: { v in
v.frame(height: _height)
})
}

private func getSafeAreaInsets() -> UIEdgeInsets {
Expand All @@ -119,6 +124,30 @@ extension OptionListPickerItem: View {
}
return keyWindow.safeAreaInsets
}

private func calculateHeight(scrollViewContentHeight: CGFloat) -> CGFloat {
let screenHeight = Screen.bounds.size.height
let safeAreaInset = self.getSafeAreaInsets()
var maxScrollViewHeight = screenHeight - self.additionalHeight()
if UIDevice.current.userInterfaceIdiom != .phone {
if self.barItemFrame.arrowDirection() == .top {
maxScrollViewHeight -= (self.barItemFrame.maxY + 80)
} else if self.barItemFrame.arrowDirection() == .bottom {
maxScrollViewHeight -= (screenHeight - self.barItemFrame.minY + 80) + safeAreaInset.bottom + 13
}
} else {
maxScrollViewHeight -= (safeAreaInset.top + 30)
}
return min(scrollViewContentHeight, maxScrollViewHeight)
}

private func additionalHeight() -> CGFloat {
let isNotIphone = UIDevice.current.userInterfaceIdiom != .phone
var height = 0.0
height += self.getSafeAreaInsets().bottom + (isNotIphone ? 13 : 16)
height += isNotIphone ? 50 : 56
return height
}
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import SwiftUI
import UIKit

public extension SearchListPickerItem {
/// create a list picker which used in FilterFeedbackBarItem
/// create a list picker which is used in FilterFeedbackBarItem
/// - Parameters:
/// - value: Selected value indexs.
/// - value: Indexes for selected values.
/// - valueOptions: The data for constructing the list picker.
/// - hint: Hint message.
/// - allowsMultipleSelection: A boolean value to indicate to allow multiple selections or not.
Expand All @@ -14,7 +14,7 @@ public extension SearchListPickerItem {
/// - updateSearchListPickerHeight: The closure to update the parent view.
/// - disableListEntriesSection: A boolean value to indicate to disable entries section or not.
/// - allowsDisplaySelectionCount: A boolean value to indicate to display selection count or not.
/// - barItemFrame: The frame of the bar item, which toggle to show this view.
/// - barItemFrame: The frame of the item in FilterFeedbackBar, which toggle to show this view.
init(value: Binding<[Int]>, valueOptions: [String] = [], hint: String? = nil, allowsMultipleSelection: Bool, allowsEmptySelection: Bool, isSearchBarHidden: Bool = false, disableListEntriesSection: Bool, allowsDisplaySelectionCount: Bool, barItemFrame: CGRect = .zero, onTap: ((_ index: Int) -> Void)? = nil, selectAll: ((_ isAll: Bool) -> Void)? = nil, updateSearchListPickerHeight: ((CGFloat) -> Void)? = nil) {
self.init(value: value, valueOptions: valueOptions, hint: hint, onTap: onTap)

Expand Down
Loading

0 comments on commit 2c6d5a4

Please sign in to comment.