|
| 1 | +import FioriSwiftUICore |
| 2 | +import Foundation |
| 3 | +import SwiftUI |
| 4 | + |
| 5 | +struct DateRangePickerExample: View { |
| 6 | + @State private var isRequired = false |
| 7 | + @State private var customizedMandatoryIndicator = false |
| 8 | + @State private var showsErrorMessage = false |
| 9 | + @State private var showAINotice: Bool = false |
| 10 | + @State private var pickerVisible0 = false |
| 11 | + @State private var pickerVisible1 = false |
| 12 | + @State private var pickerVisible2 = false |
| 13 | + @State private var pickerVisible3 = false |
| 14 | + @State private var pickerVisible4 = false |
| 15 | + @State private var pickerVisible5 = false |
| 16 | + |
| 17 | + // Limit the selectable dates from last seven days to next seven days |
| 18 | + private var limitDateRange: Range<Date> = Date(timeIntervalSinceNow: -60 * 60 * 24 * 7) ..< Date(timeIntervalSinceNow: 60 * 60 * 24 * 7) |
| 19 | + |
| 20 | + struct CustomTitleStyle: TitleStyle { |
| 21 | + func makeBody(_ configuration: TitleConfiguration) -> some View { |
| 22 | + Title(configuration) |
| 23 | + .font(.fiori(forTextStyle: .title3)) |
| 24 | + .foregroundStyle(Color.preferredColor(.indigo7)) |
| 25 | + } |
| 26 | + } |
| 27 | + |
| 28 | + struct CustomValueLabelStyle: ValueLabelStyle { |
| 29 | + func makeBody(_ configuration: ValueLabelConfiguration) -> some View { |
| 30 | + ValueLabel(configuration) |
| 31 | + .font(.fiori(forTextStyle: .callout)) |
| 32 | + .foregroundStyle(Color.preferredColor(.green7)) |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + var managePickerVisibleBinding: Binding<Bool> { |
| 37 | + Binding { |
| 38 | + [self.pickerVisible0, self.pickerVisible1, self.pickerVisible2, self.pickerVisible3, self.pickerVisible4, self.pickerVisible5].allSatisfy { $0 } |
| 39 | + } set: { newValue in |
| 40 | + self.pickerVisible0 = newValue |
| 41 | + self.pickerVisible1 = newValue |
| 42 | + self.pickerVisible2 = newValue |
| 43 | + self.pickerVisible3 = newValue |
| 44 | + self.pickerVisible4 = newValue |
| 45 | + self.pickerVisible5 = newValue |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + func mandatoryFieldIndicator(_ disabled: Bool = false) -> TextOrIcon { |
| 50 | + var indicator = AttributedString(self.customizedMandatoryIndicator ? "#" : "*") |
| 51 | + if self.customizedMandatoryIndicator, !disabled { |
| 52 | + indicator.font = .fiori(forTextStyle: .title3) |
| 53 | + indicator.foregroundColor = Color.preferredColor(.indigo7) |
| 54 | + } |
| 55 | + return .text(indicator) |
| 56 | + } |
| 57 | + |
| 58 | + @State var selectedRange0: ClosedRange<Date>? |
| 59 | + @State var selectedRange1: ClosedRange<Date>? = Date.now ... Date(timeIntervalSinceNow: 24 * 60 * 60 * 2) |
| 60 | + @State var selectedRange2: ClosedRange<Date>? |
| 61 | + @State var selectedRange3: ClosedRange<Date>? = Date.now ... Date(timeIntervalSinceNow: 24 * 60 * 60 * 80) |
| 62 | + @State var selectedRange4: ClosedRange<Date>? = Date.now ... Date(timeIntervalSinceNow: 24 * 60 * 60 * 10) |
| 63 | + @State var selectedRange5: ClosedRange<Date>? = Date.now ... Date.now |
| 64 | + |
| 65 | + let customizedDateFormatter: DateFormatter = { |
| 66 | + let formatter = DateFormatter() |
| 67 | + formatter.dateFormat = "dd-MM-yyyy" |
| 68 | + return formatter |
| 69 | + }() |
| 70 | + |
| 71 | + var body: some View { |
| 72 | + List { |
| 73 | + Toggle("Mandatory Field", isOn: self.$isRequired) |
| 74 | + .tint(Color.preferredColor(.tintColor)) |
| 75 | + if self.isRequired { |
| 76 | + Toggle("Customized Mandatory Indicator", isOn: self.$customizedMandatoryIndicator) |
| 77 | + .tint(Color.preferredColor(.tintColor)) |
| 78 | + } |
| 79 | + Toggle("Show Error/Hint message", isOn: self.$showsErrorMessage) |
| 80 | + .tint(Color.preferredColor(.tintColor)) |
| 81 | + Toggle("AI Notice", isOn: self.$showAINotice) |
| 82 | + .tint(Color.preferredColor(.tintColor)) |
| 83 | + Toggle("Picker Visible", isOn: self.managePickerVisibleBinding) |
| 84 | + .tint(Color.preferredColor(.tintColor)) |
| 85 | + Section(header: Text("")) { |
| 86 | + DateRangePicker(title: "Range Selection Long Title Long Title Long Title Long Title Long Title Long Title0", mandatoryFieldIndicator: self.mandatoryFieldIndicator(), isRequired: self.isRequired, selectedRange: self.$selectedRange0, pickerVisible: self.$pickerVisible0) |
| 87 | + .informationView(isPresented: self.$showsErrorMessage, description: AttributedString("This is information error message.")) |
| 88 | + .informationViewStyle(.error) |
| 89 | + .aiNoticeView(isPresented: self.$showAINotice, description: "AI Notice") |
| 90 | + |
| 91 | + DateRangePicker(title: "Range Selection1", mandatoryFieldIndicator: self.mandatoryFieldIndicator(), isRequired: self.isRequired, selectedRange: self.$selectedRange1, pickerVisible: self.$pickerVisible1) |
| 92 | + .informationView(isPresented: self.$showsErrorMessage, description: AttributedString("This is information hint message.")) |
| 93 | + .informationViewStyle(.informational) |
| 94 | + .aiNoticeView(isPresented: self.$showAINotice, description: "AI Notice") |
| 95 | + .titleStyle(CustomTitleStyle()) |
| 96 | + .valueLabelStyle(CustomValueLabelStyle()) |
| 97 | + |
| 98 | + DateRangePicker(title: "Limit inclusive range of selectable dates", mandatoryFieldIndicator: self.mandatoryFieldIndicator(), isRequired: self.isRequired, range: self.limitDateRange, selectedRange: self.$selectedRange2, noRangeSelectedString: "Please select range", pickerVisible: self.$pickerVisible2) |
| 99 | + .informationView(isPresented: self.$showsErrorMessage, description: AttributedString("This is information warning message.")) |
| 100 | + .informationViewStyle(.warning) |
| 101 | + .aiNoticeView(isPresented: self.$showAINotice, description: "AI Notice") |
| 102 | + |
| 103 | + DateRangePicker(title: "Customized Date Formatter", mandatoryFieldIndicator: self.mandatoryFieldIndicator(), isRequired: self.isRequired, selectedRange: self.$selectedRange3, rangeFormatter: self.customizedDateFormatter, pickerVisible: self.$pickerVisible3) |
| 104 | + .informationView(isPresented: self.$showsErrorMessage, description: AttributedString("This is information success message.")) |
| 105 | + .informationViewStyle(.success) |
| 106 | + .aiNoticeView(isPresented: self.$showAINotice, description: "AI Notice") |
| 107 | + |
| 108 | + DateRangePicker(title: "Custom Locale & Calendar", mandatoryFieldIndicator: self.mandatoryFieldIndicator(), isRequired: self.isRequired, selectedRange: self.$selectedRange4, pickerVisible: self.$pickerVisible4) |
| 109 | + .informationView(isPresented: self.$showsErrorMessage, description: AttributedString("This is information hint message.")) |
| 110 | + .informationViewStyle(.informational) |
| 111 | + .aiNoticeView(isPresented: self.$showAINotice, description: "AI Notice") |
| 112 | + .environment(\.locale, Locale(identifier: "zh-Hans")) |
| 113 | + .environment(\.calendar, Calendar(identifier: .gregorian)) |
| 114 | + |
| 115 | + DateRangePicker(title: "Range Selection in Disabled Control State", mandatoryFieldIndicator: self.mandatoryFieldIndicator(true), isRequired: self.isRequired, controlState: .disabled, selectedRange: self.$selectedRange5, pickerVisible: self.$pickerVisible5) |
| 116 | + .informationView(isPresented: self.$showsErrorMessage, description: AttributedString("This is information success message.")) |
| 117 | + .informationViewStyle(.success) |
| 118 | + .aiNoticeView(isPresented: self.$showAINotice, description: "AI Notice") |
| 119 | + } |
| 120 | + } |
| 121 | + .onChange(of: self.selectedRange0) { _, newValue in |
| 122 | + print("selectedRange0 new Value:\(self.getValueLabel(newValue))") |
| 123 | + } |
| 124 | + .onChange(of: self.selectedRange1) { _, newValue in |
| 125 | + print("selectedRange1 new Value:\(self.getValueLabel(newValue))") |
| 126 | + } |
| 127 | + .onChange(of: self.selectedRange2) { _, newValue in |
| 128 | + print("selectedRange2 new Value:\(self.getValueLabel(newValue))") |
| 129 | + } |
| 130 | + .onChange(of: self.selectedRange3) { _, newValue in |
| 131 | + print("selectedRange3 new Value:\(self.getValueLabel(newValue))") |
| 132 | + } |
| 133 | + .onChange(of: self.selectedRange4) { _, newValue in |
| 134 | + print("selectedRange4 new Value:\(self.getValueLabel(newValue))") |
| 135 | + } |
| 136 | + .onChange(of: self.selectedRange5) { _, newValue in |
| 137 | + print("selectedRange5 new Value:\(self.getValueLabel(newValue))") |
| 138 | + } |
| 139 | + .navigationTitle("Date Range Picker") |
| 140 | + } |
| 141 | + |
| 142 | + private func getValueLabel(_ selectedRange: ClosedRange<Date>?) -> String { |
| 143 | + if let startDate = selectedRange?.lowerBound, |
| 144 | + let endDate = selectedRange?.upperBound |
| 145 | + { |
| 146 | + let valueDescDateFormatter = DateFormatter() |
| 147 | + valueDescDateFormatter.timeZone = Calendar.current.timeZone |
| 148 | + valueDescDateFormatter.locale = Calendar.current.locale |
| 149 | + valueDescDateFormatter.dateStyle = .short |
| 150 | + valueDescDateFormatter.timeStyle = .none |
| 151 | + let startDateStr = valueDescDateFormatter.string(from: startDate) |
| 152 | + let endDateStr = valueDescDateFormatter.string(from: endDate) |
| 153 | + |
| 154 | + return "\(startDateStr) – \(endDateStr)" |
| 155 | + } else { |
| 156 | + return "No range selected" |
| 157 | + } |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +struct DateRangePickerExample_Previews: PreviewProvider { |
| 162 | + static var previews: some View { |
| 163 | + DateRangePickerExample() |
| 164 | + } |
| 165 | +} |
0 commit comments