@@ -4,9 +4,13 @@ struct SettingsOtherTabView: View {
44 @ObservedObject var viewModel : OtherSettingsViewModel
55 @ObservedObject var launchAtLoginManager : LaunchAtLoginManager
66 @FocusState private var isOutputLanguageFocused : Bool
7- @State private var isExportStartDatePickerPresented = false
8- @State private var isExportEndDatePickerPresented = false
9- @State private var isReprocessDatePickerPresented = false
7+ @State private var activeExportDatePicker : ExportDatePicker ?
8+ @State private var isReprocessDatePickerExpanded = false
9+
10+ private enum ExportDatePicker {
11+ case start
12+ case end
13+ }
1014
1115 var body : some View {
1216 VStack ( alignment: . leading, spacing: 28 ) {
@@ -142,14 +146,16 @@ struct SettingsOtherTabView: View {
142146
143147 VStack ( alignment: . leading, spacing: 14 ) {
144148 HStack ( alignment: . bottom, spacing: 12 ) {
145- datePopoverField (
149+ datePillField (
146150 label: " From " ,
147- date: $ viewModel. exportStartDate,
148- isPresented : $isExportStartDatePickerPresented ,
151+ date: viewModel. exportStartDate,
152+ isExpanded : activeExportDatePicker == . start ,
149153 accessibilityLabel: " Export start date " ,
150- onOpen: {
151- isExportEndDatePickerPresented = false
152- isReprocessDatePickerPresented = false
154+ onTap: {
155+ withAnimation ( . spring( response: 0.24 , dampingFraction: 0.88 ) ) {
156+ activeExportDatePicker = activeExportDatePicker == . start ? nil : . start
157+ isReprocessDatePickerExpanded = false
158+ }
153159 }
154160 )
155161
@@ -158,16 +164,31 @@ struct SettingsOtherTabView: View {
158164 . foregroundColor ( . black. opacity ( 0.35 ) )
159165 . padding ( . bottom, 12 )
160166
161- datePopoverField (
167+ datePillField (
162168 label: " To " ,
163- date: $ viewModel. exportEndDate,
164- isPresented : $isExportEndDatePickerPresented ,
169+ date: viewModel. exportEndDate,
170+ isExpanded : activeExportDatePicker == . end ,
165171 accessibilityLabel: " Export end date " ,
166- onOpen: {
167- isExportStartDatePickerPresented = false
168- isReprocessDatePickerPresented = false
172+ onTap: {
173+ withAnimation ( . spring( response: 0.24 , dampingFraction: 0.88 ) ) {
174+ activeExportDatePicker = activeExportDatePicker == . end ? nil : . end
175+ isReprocessDatePickerExpanded = false
176+ }
177+ }
178+ )
179+ }
180+
181+ if let activeExportDatePicker {
182+ inlineCalendarField (
183+ label: activeExportDatePicker == . start ? " Start date " : " End date " ,
184+ date: exportDateBinding ( for: activeExportDatePicker) ,
185+ onDateSelected: {
186+ withAnimation ( . spring( response: 0.24 , dampingFraction: 0.88 ) ) {
187+ self . activeExportDatePicker = nil
188+ }
169189 }
170190 )
191+ . transition ( . move( edge: . top) . combined ( with: . opacity) )
171192 }
172193
173194 Text ( " Includes titles, summaries, and details for each card. " )
@@ -231,17 +252,34 @@ struct SettingsOtherTabView: View {
231252
232253 VStack ( alignment: . leading, spacing: 14 ) {
233254 VStack ( alignment: . leading, spacing: 8 ) {
234- datePopoverField (
255+ datePillField (
235256 label: " Day " ,
236- date: $ viewModel. reprocessDayDate,
237- isPresented : $isReprocessDatePickerPresented ,
257+ date: viewModel. reprocessDayDate,
258+ isExpanded : isReprocessDatePickerExpanded ,
238259 accessibilityLabel: " Reprocess day " ,
239260 disabled: viewModel. isReprocessingDay,
240- onOpen: {
241- isExportStartDatePickerPresented = false
242- isExportEndDatePickerPresented = false
261+ onTap: {
262+ withAnimation ( . spring( response: 0.24 , dampingFraction: 0.88 ) ) {
263+ isReprocessDatePickerExpanded. toggle ( )
264+ activeExportDatePicker = nil
265+ }
243266 }
244267 )
268+
269+ if isReprocessDatePickerExpanded {
270+ inlineCalendarField (
271+ label: " Day " ,
272+ date: $viewModel. reprocessDayDate,
273+ disabled: viewModel. isReprocessingDay,
274+ onDateSelected: {
275+ withAnimation ( . spring( response: 0.24 , dampingFraction: 0.88 ) ) {
276+ isReprocessDatePickerExpanded = false
277+ }
278+ }
279+ )
280+ . transition ( . move( edge: . top) . combined ( with: . opacity) )
281+ }
282+
245283 Text ( dayString)
246284 . font ( . custom( " Nunito " , size: 12 ) )
247285 . foregroundColor ( . black. opacity ( 0.5 ) )
@@ -308,14 +346,22 @@ struct SettingsOtherTabView: View {
308346 Self . dateLabelFormatter. string ( from: timelineDisplayDate ( from: date) )
309347 }
310348
311- @ViewBuilder
312- private func datePopoverField(
349+ private func exportDateBinding( for picker: ExportDatePicker ) -> Binding < Date > {
350+ switch picker {
351+ case . start:
352+ return $viewModel. exportStartDate
353+ case . end:
354+ return $viewModel. exportEndDate
355+ }
356+ }
357+
358+ private func datePillField(
313359 label: String ,
314- date: Binding < Date > ,
315- isPresented : Binding < Bool > ,
360+ date: Date ,
361+ isExpanded : Bool ,
316362 accessibilityLabel: String ,
317363 disabled: Bool = false ,
318- onOpen : @escaping ( ) -> Void
364+ onTap : @escaping ( ) -> Void
319365 ) -> some View {
320366 VStack ( alignment: . leading, spacing: 7 ) {
321367 Text ( label)
@@ -324,21 +370,20 @@ struct SettingsOtherTabView: View {
324370
325371 Button {
326372 guard !disabled else { return }
327- onOpen ( )
328- isPresented. wrappedValue. toggle ( )
373+ onTap ( )
329374 } label: {
330375 HStack ( spacing: 10 ) {
331376 Image ( systemName: " calendar " )
332377 . font ( . system( size: 12 , weight: . semibold) )
333378 . foregroundColor ( Color ( red: 0.25 , green: 0.17 , blue: 0 ) . opacity ( disabled ? 0.4 : 0.75 ) )
334379
335- Text ( formattedTimelineDate ( date. wrappedValue ) )
380+ Text ( formattedTimelineDate ( date) )
336381 . font ( . custom( " Nunito " , size: 14 ) )
337382 . foregroundColor ( . black. opacity ( disabled ? 0.35 : 0.82 ) )
338383
339384 Spacer ( minLength: 4 )
340385
341- Image ( systemName: isPresented . wrappedValue ? " chevron.up " : " chevron.down " )
386+ Image ( systemName: isExpanded ? " chevron.up " : " chevron.down " )
342387 . font ( . system( size: 11 , weight: . semibold) )
343388 . foregroundColor ( . black. opacity ( disabled ? 0.2 : 0.35 ) )
344389 }
@@ -351,7 +396,7 @@ struct SettingsOtherTabView: View {
351396 . overlay (
352397 RoundedRectangle ( cornerRadius: 10 )
353398 . stroke (
354- isPresented . wrappedValue
399+ isExpanded
355400 ? Color ( hex: " F9C36B " )
356401 : Color ( hex: " FFE0A5 " ) ,
357402 lineWidth: 1.2
@@ -363,32 +408,40 @@ struct SettingsOtherTabView: View {
363408 . buttonStyle ( . plain)
364409 . disabled ( disabled)
365410 . accessibilityLabel ( Text ( accessibilityLabel) )
366- . popover ( isPresented: isPresented, arrowEdge: . bottom) {
367- VStack ( alignment: . leading, spacing: 10 ) {
368- HStack {
369- Text ( label)
370- . font ( . custom( " Nunito " , size: 13 ) )
371- . foregroundColor ( . black. opacity ( 0.65 ) )
372- Spacer ( )
373- Button ( " Done " ) {
374- isPresented. wrappedValue = false
375- }
376- . buttonStyle ( . plain)
377- . font ( . custom( " Nunito " , size: 12 ) )
378- . foregroundColor ( Color ( red: 0.25 , green: 0.17 , blue: 0 ) )
379- }
411+ }
412+ }
380413
381- DatePicker ( " " , selection: date, displayedComponents: . date)
382- . datePickerStyle ( . graphical)
383- . labelsHidden ( )
384- . onChange ( of: date. wrappedValue) { _, _ in
385- isPresented. wrappedValue = false
386- }
414+ private func inlineCalendarField(
415+ label: String ,
416+ date: Binding < Date > ,
417+ disabled: Bool = false ,
418+ onDateSelected: @escaping ( ) -> Void
419+ ) -> some View {
420+ VStack ( alignment: . leading, spacing: 6 ) {
421+ Text ( label)
422+ . font ( . custom( " Nunito " , size: 11.5 ) )
423+ . foregroundColor ( . black. opacity ( 0.5 ) )
424+
425+ DatePicker ( " " , selection: date, displayedComponents: . date)
426+ . datePickerStyle ( . graphical)
427+ . labelsHidden ( )
428+ . onChange ( of: date. wrappedValue) { _, _ in
429+ onDateSelected ( )
387430 }
388- . padding ( 14 )
389- . frame ( width: 300 )
390- }
431+ . frame ( maxWidth: 290 , alignment: . leading)
432+ . padding ( 10 )
433+ . background (
434+ RoundedRectangle ( cornerRadius: 12 )
435+ . fill ( Color . white. opacity ( disabled ? 0.45 : 0.82 ) )
436+ . overlay (
437+ RoundedRectangle ( cornerRadius: 12 )
438+ . stroke ( Color ( hex: " FFE0A5 " ) , lineWidth: 1.2 )
439+ )
440+ )
441+ . shadow ( color: . black. opacity ( disabled ? 0 : 0.04 ) , radius: 7 , x: 0 , y: 2 )
442+ . disabled ( disabled)
391443 }
444+ . opacity ( disabled ? 0.7 : 1 )
392445 }
393446
394447 private static let dateLabelFormatter : DateFormatter = {
0 commit comments