Skip to content

Commit

Permalink
Merge branch 'main' into donburi/DON-628-calendar-changes-poc-second
Browse files Browse the repository at this point in the history
  • Loading branch information
novinfard committed Nov 14, 2024
2 parents 171547e + 304fe79 commit 5e3596a
Show file tree
Hide file tree
Showing 19 changed files with 156 additions and 69 deletions.
5 changes: 5 additions & 0 deletions Backpack-SwiftUI/Calendar/Classes/BPKCalendar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import SwiftUI
/// the `Calendar` struct in Swift.
/// - validRange: The range of dates that the calendar should allow the user to select.
/// This is specified as a`ClosedRange<Date>`.
/// - initialMonthScroll: The initial scrolling to the month using `MonthScroll`
///
/// The `BPKCalendar` view also allows you to specify an accessory action. This is a closure that takes a string and
/// a date, and is called when the user interacts with an accessory in the calendar.
Expand All @@ -37,6 +38,7 @@ public struct BPKCalendar<DayAccessoryView: View>: View {
let selectionType: CalendarSelectionType
let validRange: ClosedRange<Date>
private var accessoryAction: ((Date) -> CalendarMonthAccessoryAction?)?
private var initialMonthScroll: MonthScroll?
private let monthHeaderDateFormatter: DateFormatter

private let dayAccessoryView: (Date) -> DayAccessoryView
Expand All @@ -46,13 +48,15 @@ public struct BPKCalendar<DayAccessoryView: View>: View {
selectionType: CalendarSelectionType,
calendar: Calendar,
validRange: ClosedRange<Date>,
initialMonthScroll: MonthScroll? = nil,
dayAccessoryView: @escaping (Date) -> DayAccessoryView = { _ in EmptyView() }
) {
self.dayAccessoryView = dayAccessoryView
_currentlyShownMonth = State(initialValue: validRange.lowerBound)
self.validRange = validRange
self.calendar = calendar
self.selectionType = selectionType
self.initialMonthScroll = initialMonthScroll

monthHeaderDateFormatter = DateFormatter()
monthHeaderDateFormatter.timeZone = calendar.timeZone
Expand All @@ -72,6 +76,7 @@ public struct BPKCalendar<DayAccessoryView: View>: View {
selectionType: selectionType,
calendar: calendar,
validRange: validRange,
monthScroll: initialMonthScroll,
monthHeader: { monthDate in
CalendarMonthHeader(
monthDate: monthDate,
Expand Down
48 changes: 40 additions & 8 deletions Backpack-SwiftUI/Calendar/Classes/Core/CalendarContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,49 @@ import SwiftUI
struct CalendarContainer<MonthContent: View>: View {
let calendar: Calendar
let validRange: ClosedRange<Date>
let monthScroll: MonthScroll?
@ViewBuilder let monthContent: (_ month: Date) -> MonthContent


@State private var hasScrolledToItem = false

var body: some View {
ScrollView {
VStack(spacing: BPKSpacing.none) {
ForEach(0...monthsToShow, id: \.self) { monthIndex in
let firstDayOfMonth = firstDayOf(monthIndex: monthIndex)
monthContent(firstDayOfMonth)
ScrollViewReader { proxy in
ScrollView {
VStack(spacing: BPKSpacing.none) {
ForEach(0...monthsToShow, id: \.self) { monthIndex in
let firstDayOfMonth = firstDayOf(monthIndex: monthIndex)
monthContent(firstDayOfMonth)
.if(monthScroll != nil, transform: { view in
view.id(scrollId(date: firstDayOfMonth))
})
}
.onAppear {
scrollIfNeeded(scrollProxy: proxy)
}
}
}
}
}


/// Generates a unique identifier for a given `Date` using the "yyyy-MM" format.
/// - Example: For a date of December 25, 2024, this method returns `"2024-12"`.
private func scrollId(date: Date) -> String? {
return monthScroll?.formatter.string(from: date)
}

private func scrollIfNeeded(scrollProxy: ScrollViewProxy) {
guard !hasScrolledToItem, let monthScroll else { return }
hasScrolledToItem = true
let scrollAction = {
scrollProxy.scrollTo(
monthScroll.scrollId,
anchor: monthScroll.anchor
)
}

monthScroll.animated ? withAnimation { scrollAction() } : scrollAction()
}

private var monthsToShow: Int {
let firstMonthComponents = calendar.dateComponents([.year, .month], from: validRange.lowerBound)
let firstMonth = calendar.date(from: firstMonthComponents)!
Expand All @@ -60,10 +90,12 @@ struct CalendarContainer_Previews: PreviewProvider {
let calendar = Calendar.current
let start = calendar.date(from: .init(year: 2023, month: 10, day: 30))!
let end = calendar.date(from: .init(year: 2025, month: 12, day: 25))!

let monthScroll = MonthScroll(monthToScroll: start)

CalendarContainer(
calendar: calendar,
validRange: start...end,
monthScroll: monthScroll,
monthContent: { monthNumber in
VStack {
BPKText("Calendar Grid \(monthNumber)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct CalendarTypeContainerFactory<MonthHeader: View, DayAccessoryView: View>:
let selectionType: CalendarSelectionType
let calendar: Calendar
let validRange: ClosedRange<Date>
let monthScroll: MonthScroll?
@ViewBuilder let monthHeader: (_ monthDate: Date) -> MonthHeader
@ViewBuilder let dayAccessoryView: (Date) -> DayAccessoryView

Expand All @@ -31,7 +32,7 @@ struct CalendarTypeContainerFactory<MonthHeader: View, DayAccessoryView: View>:
formatter.dateStyle = .full
return formatter
}

var body: some View {
switch selectionType {
case .range(let selection, let accessibilityConfigurations):
Expand All @@ -43,6 +44,7 @@ struct CalendarTypeContainerFactory<MonthHeader: View, DayAccessoryView: View>:
accessibilityConfigurations: accessibilityConfigurations,
dateFormatter: accessibilityDateFormatter
),
monthScroll: monthScroll,
monthHeader: monthHeader,
dayAccessoryView: dayAccessoryView
)
Expand All @@ -55,6 +57,7 @@ struct CalendarTypeContainerFactory<MonthHeader: View, DayAccessoryView: View>:
accessibilityConfigurations: accessibilityConfigurations,
dateFormatter: accessibilityDateFormatter
),
monthScroll: monthScroll,
monthHeader: monthHeader,
dayAccessoryView: dayAccessoryView
)
Expand Down
48 changes: 48 additions & 0 deletions Backpack-SwiftUI/Calendar/Classes/Core/MonthScroll.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Backpack - Skyscanner's Design System
*
* Copyright 2018 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import SwiftUI

/// Represents a scrollable month with a unique identifier and anchor point for positioning.
public struct MonthScroll {
/// Unique identifier for the scroll item.
let scrollId: String

/// Anchor point for the scroll position, defaulting to `.top`.
let anchor: UnitPoint

/// Whether to animate the change in scroll position or not
let animated: Bool

/// The `DateFormatter` to format dates as "yyyy-MM" strings, used for generating `scrollId`s.
/// - Example: For a date of December 25, 2024, it produces `"2024-12"`.
let formatter: DateFormatter

// MARK: - Initialiser

/// Initialises `MonthScroll` using a `Date` to generate the `scrollId`, with `anchor`.
public init(monthToScroll: Date, anchor: UnitPoint = .top, animated: Bool = false) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM"

self.scrollId = dateFormatter.string(from: monthToScroll)
self.anchor = anchor
self.animated = animated
self.formatter = dateFormatter
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct RangeCalendarContainer<MonthHeader: View, DayAccessoryView: View>: View {
let calendar: Calendar
let validRange: ClosedRange<Date>
let accessibilityProvider: RangeDayAccessibilityProvider
let monthScroll: MonthScroll?
@ViewBuilder let monthHeader: (_ monthDate: Date) -> MonthHeader
@ViewBuilder let dayAccessoryView: (Date) -> DayAccessoryView

Expand Down Expand Up @@ -119,7 +120,8 @@ struct RangeCalendarContainer<MonthHeader: View, DayAccessoryView: View>: View {
var body: some View {
CalendarContainer(
calendar: calendar,
validRange: validRange
validRange: validRange,
monthScroll: monthScroll
) { month in
monthHeader(month)
CalendarMonthGrid(
Expand Down Expand Up @@ -187,7 +189,7 @@ struct RangeCalendarContainer_Previews: PreviewProvider {

let startSelection = calendar.date(from: .init(year: 2023, month: 10, day: 30))!
let endSelection = calendar.date(from: .init(year: 2023, month: 11, day: 10))!

RangeCalendarContainer(
selectionState: .constant(.range(startSelection...endSelection)),
calendar: calendar,
Expand All @@ -204,6 +206,7 @@ struct RangeCalendarContainer_Previews: PreviewProvider {
),
dateFormatter: Self.formatter
),
monthScroll: nil,
monthHeader: { month in
BPKText("\(Self.formatter.string(from: month))")
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct SingleCalendarContainer<MonthHeader: View, DayAccessoryView: View>: View
let calendar: Calendar
let validRange: ClosedRange<Date>
let accessibilityProvider: SingleDayAccessibilityProvider
let monthScroll: MonthScroll?
@ViewBuilder let monthHeader: (_ monthDate: Date) -> MonthHeader
@ViewBuilder let dayAccessoryView: (Date) -> DayAccessoryView

Expand All @@ -44,7 +45,11 @@ struct SingleCalendarContainer<MonthHeader: View, DayAccessoryView: View>: View
}

var body: some View {
CalendarContainer(calendar: calendar, validRange: validRange) { month in
CalendarContainer(
calendar: calendar,
validRange: validRange,
monthScroll: monthScroll
) { month in
monthHeader(month)
CalendarMonthGrid(
monthDate: month,
Expand All @@ -70,7 +75,7 @@ struct SingleCalendarContainer_Previews: PreviewProvider {
let calendar = Calendar.current
let start = calendar.date(from: .init(year: 2023, month: 10, day: 30))!
let end = calendar.date(from: .init(year: 2025, month: 12, day: 25))!

SingleCalendarContainer(
selection: .constant(calendar.date(from: .init(year: 2023, month: 11, day: 10))!),
calendar: calendar,
Expand All @@ -79,6 +84,7 @@ struct SingleCalendarContainer_Previews: PreviewProvider {
accessibilityConfigurations: .init(selectionHint: ""),
dateFormatter: Self.formatter
),
monthScroll: nil,
monthHeader: { month in
BPKText("\(Self.formatter.string(from: month))")
},
Expand Down
3 changes: 2 additions & 1 deletion Backpack-SwiftUI/StarRating/Classes/BPKHotelStarRating.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public struct BPKHotelStarRating: View {
public var body: some View {
HStack(spacing: 0) {
ForEach(0..<rating, id: \.self) { _ in
BPKStarView(type: .full, size: size.starSize)
BPKIconView(.star, size: size.starSize)
.foregroundColor(.textSecondaryColor)
.accessibilityHidden(true)
}
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 2 additions & 18 deletions Example/Backpack.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
53C6622929EA0DAB00BF1A62 /* AttributedTextExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C6620F29EA0DAB00BF1A62 /* AttributedTextExampleView.swift */; };
53C7F0B92A4C615D003A8740 /* ChipGroupSingleSelectRailExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C7F0B52A4C615D003A8740 /* ChipGroupSingleSelectRailExampleView.swift */; };
53C7F0BA2A4C615D003A8740 /* ChipGroupSingleSelectWrapExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C7F0B62A4C615D003A8740 /* ChipGroupSingleSelectWrapExampleView.swift */; };
53D4614E29E574EB0061C222 /* (null) in Sources */ = {isa = PBXBuildFile; };
53D4614E29E574EB0061C222 /* BuildFile in Sources */ = {isa = PBXBuildFile; };
53D6396F2A7D46ED007EF376 /* MapMarkerExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D6396E2A7D46ED007EF376 /* MapMarkerExampleView.swift */; };
53D7FBB527F715AF00DEE588 /* UIWindowFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D7FBB427F715AF00DEE588 /* UIWindowFactory.swift */; };
53E075A527FC780C0033147C /* NavBarGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E075A427FC780C0033147C /* NavBarGroups.swift */; };
Expand Down Expand Up @@ -1650,7 +1650,6 @@
6003F588195388D20070C39A /* Resources */,
84EFE62BE1534CD46CAFBDBF /* [CP] Embed Pods Frameworks */,
D68AF8FA2174CB9C00BA743D /* Run Script */,
EFF4B5AAADA028B292046E74 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -1837,21 +1836,6 @@
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\" lint --strict\n";
};
EFF4B5AAADA028B292046E74 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Backpack-Native/Pods-Backpack-Native-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
Expand Down Expand Up @@ -1957,7 +1941,7 @@
5390DB612909940700F0F790 /* ColorTokensViewController.swift in Sources */,
794E1FF8280CF63100B965FF /* RadiusTokensView.swift in Sources */,
53B6DB6A27FC73A90042B7C0 /* LabelGroups.swift in Sources */,
53D4614E29E574EB0061C222 /* (null) in Sources */,
53D4614E29E574EB0061C222 /* BuildFile in Sources */,
53C6621329EA0DAB00BF1A62 /* SpinnerExampleView.swift in Sources */,
53BAC3F12A71415800236CC9 /* SectionHeaderGroups.swift in Sources */,
53C6622429EA0DAB00BF1A62 /* ButtonsPlaygroundView.swift in Sources */,
Expand Down
7 changes: 0 additions & 7 deletions Example/Backpack/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,12 @@
*/

import UIKit
import AppCenter
import AppCenterDistribute
import AppCenterAnalytics
import AppCenterCrashes

import Backpack
import Backpack_SwiftUI

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// swiftlint:disable indentation_width
private func setupAppearance() {
UINavigationBar.appearance().tintColor = BPKColor.textPrimaryColor
BPKAppearance.apply()
Expand All @@ -51,8 +46,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
) -> Bool {
setupRelativeFont()
setupAppearance()
AppCenter.start(withAppSecret: "$(APP_CENTER_SECRET)",
services: [Analytics.self, Crashes.self, Distribute.self])
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import Backpack_SwiftUI

struct CalendarExampleRangeView: View {
@State var selection: CalendarRangeSelectionState?

private let monthScroll: MonthScroll?

let validRange: ClosedRange<Date>
let calendar: Calendar
let formatter: DateFormatter
let showAccessoryViews: Bool
init(showAccessoryViews: Bool) {

init(showAccessoryViews: Bool, makeInitialMonthScroll: Bool = false) {
let calendar = Calendar.current
let start = calendar.date(from: .init(year: 2023, month: 11, day: 6))!
let end = calendar.date(from: .init(year: 2024, month: 11, day: 28))!
Expand All @@ -42,8 +43,17 @@ struct CalendarExampleRangeView: View {
formatter.timeZone = calendar.timeZone
self.formatter = formatter
self.showAccessoryViews = showAccessoryViews
let selectionStart = calendar.date(from: .init(year: 2023, month: 11, day: 23))!
let selectionEnd = calendar.date(from: .init(year: 2023, month: 12, day: 2))!
var selectionStart = calendar.date(from: .init(year: 2023, month: 11, day: 23))!
var selectionEnd = calendar.date(from: .init(year: 2023, month: 12, day: 2))!

if makeInitialMonthScroll {
selectionStart = calendar.date(from: .init(year: 2024, month: 2, day: 5))!
selectionEnd = calendar.date(from: .init(year: 2024, month: 2, day: 10))!
self.monthScroll = .init(monthToScroll: selectionStart)
} else {
self.monthScroll = nil
}

_selection = State(initialValue: .range(selectionStart...selectionEnd))
}

Expand Down Expand Up @@ -98,7 +108,8 @@ struct CalendarExampleRangeView: View {
accessibilityConfigurations: accessibilityConfigurations
),
calendar: calendar,
validRange: validRange
validRange: validRange,
initialMonthScroll: monthScroll
)
}
}
Expand Down
Loading

0 comments on commit 5e3596a

Please sign in to comment.