diff --git a/Apps/Examples/Examples/FioriSwiftUICore/FormViews/RatingControlFormViewExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/FormViews/RatingControlFormViewExample.swift index dceb875f7..1ec67ffae 100644 --- a/Apps/Examples/Examples/FioriSwiftUICore/FormViews/RatingControlFormViewExample.swift +++ b/Apps/Examples/Examples/FioriSwiftUICore/FormViews/RatingControlFormViewExample.swift @@ -3,97 +3,247 @@ import SwiftUI struct RatingControlFormViewExample: View { @State var rating1: Int = 1 - @State var rating2: Int = 2 - @State var rating3: Int = 3 - @State var rating4: Int = 4 - - @State var rating5: Int = 1 - - @State var rating6: Int = 2 - - @State var rating7: Int = 3 - - @State var rating8: Int = 4 - - @State var rating9: Int = 1 - - @State var rating10: Int = 2 - - @State var rating11: Int = 3 - - @State var rating12: Int = 4 - - @State var showsErrorMessage = false - @State var usesCustomColors = false - @State var usesCustomImages = false + @State var rating5: Int = 5 + @State var rating6: Int = 1 + @State var rating7: Int = 2 + @State var rating8: Int = 3 + @State var rating9: Int = 4 + @State var rating10: Int = 5 + @State var rating11: Int = 1 + @State var rating12: Int = 2 + @State var rating13: Int = 3 + @State var rating14: Int = 4 + @State var rating15: Int = 5 + @State var rating16: Int = 1 + @State var rating17: Int = 2 + @State var rating18: Int = 3 + @State var rating19: Int = 3 + @State var rating20: Int = 3 + @State var rating21: Int = 3 + @State var rating22: Int = 3 + @State var rating23: Int = 3 + @State var rating24: Int = 3 + + @State var showsHintMessage = false var errorMessage = AttributedString("Error Message") + @State var showsValueLabel = false + @State var showsReviewCountLabel = false + @State var setsAverageRating = false + @State var setsReviewCount = false + @State var setsReviewCountCeiling = false + var body: some View { List { - Text("RatingControlFormView Example") - Toggle("Shows Error Message", isOn: self.$showsErrorMessage) + Text("RatingControlFormView Examples") + Toggle("Shows Hint Message", isOn: self.$showsHintMessage) + .padding(.leading, 16) + .padding(.trailing, 16) + Toggle("Shows Value Label", isOn: self.$showsValueLabel) .padding(.leading, 16) .padding(.trailing, 16) - Toggle("Custom Colors", isOn: self.$usesCustomColors) + Toggle("Shows Review Count Label", isOn: self.$showsReviewCountLabel) .padding(.leading, 16) .padding(.trailing, 16) - Toggle("Custom Images", isOn: self.$usesCustomImages) + Toggle("Sets Average Rating", isOn: self.$setsAverageRating) .padding(.leading, 16) .padding(.trailing, 16) + Toggle("Sets Review Count", isOn: self.$setsReviewCount) + .padding(.leading, 16) + .padding(.trailing, 16) + Toggle("Sets Review Count Ceiling", isOn: self.$setsReviewCountCeiling) + .padding(.leading, 16) + .padding(.trailing, 16) + Text("Default Examples") - RatingControlFormView(title: "Rating 1", rating: self.$rating1, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.normal), offColor: self.getOffColor(), errorMessage: self.getErrorMessage()) - RatingControlFormView(title: "Rating 2 (Disabled)", rating: self.$rating2, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.disabled), offColor: self.getOffColor(), controlState: .disabled, errorMessage: self.getErrorMessage()) - RatingControlFormView(title: "Rating 3 (Read Only) Rating 3 (Read Only)", rating: self.$rating3, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.readOnly), offColor: self.getOffColor(), controlState: .readOnly, errorMessage: self.getErrorMessage()) - RatingControlFormView(title: "Rating 4 (Highlighted)", rating: self.$rating4, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.highlighted), offColor: self.getOffColor(), controlState: .highlighted, errorMessage: self.getErrorMessage()) + RatingControlFormView(title: "Rating 1", rating: self.$rating1, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 2 (Disabled)", rating: self.$rating2, ratingControlStyle: .editableDisabled, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 3 (Read Only) Rating 3 (Read Only)", rating: self.$rating3, ratingControlStyle: .standard, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 4 (Read Only Large) Rating 4 (Read Only Large)", rating: self.$rating4, ratingControlStyle: .standardLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 5 (Accented)", rating: self.$rating5, ratingControlStyle: .accented, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 6 (Accented Large)", rating: self.$rating6, ratingControlStyle: .accentedLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) Text("Stacked") - RatingControlFormView(title: "Rating 5", rating: self.$rating5, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.normal), offColor: self.getOffColor(), errorMessage: self.getErrorMessage(), axis: .vertical) - RatingControlFormView(title: "Rating 6 (Disabled)", rating: self.$rating6, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.disabled), offColor: self.getOffColor(), controlState: .disabled, errorMessage: self.getErrorMessage(), axis: .vertical) - RatingControlFormView(title: "Rating 7 (Read Only)", rating: self.$rating7, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.readOnly), offColor: self.getOffColor(), controlState: .readOnly, errorMessage: self.getErrorMessage(), axis: .vertical) - RatingControlFormView(title: "Rating 8 (Highlighted)", rating: self.$rating8, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.highlighted), offColor: self.getOffColor(), controlState: .highlighted, errorMessage: self.getErrorMessage(), axis: .vertical) + RatingControlFormView(title: "Rating 7", rating: self.$rating7, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, axis: .vertical) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 8 (Disabled)", rating: self.$rating8, ratingControlStyle: .editableDisabled, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, axis: .vertical) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 9 (Read Only)", rating: self.$rating9, ratingControlStyle: .standard, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, axis: .vertical) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 10 (Read Only Large)", rating: self.$rating10, ratingControlStyle: .standardLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, axis: .vertical) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 11 (Accented)", rating: self.$rating11, ratingControlStyle: .accented, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, axis: .vertical) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 12 (Accented Large)", rating: self.$rating12, ratingControlStyle: .accentedLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, axis: .vertical) + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) Text("With Subtitle") - RatingControlFormView(title: "Rating 9", rating: self.$rating9, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.normal), offColor: self.getOffColor(), subtitle: "Rating 9 Subtitle", errorMessage: self.getErrorMessage()) - RatingControlFormView(title: "Rating 10 (Disabled)", rating: self.$rating10, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.disabled), offColor: self.getOffColor(), subtitle: "Rating 10 Subtitle", controlState: .disabled, errorMessage: self.getErrorMessage()) - RatingControlFormView(title: "Rating 11 (Read Only)", rating: self.$rating11, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.readOnly), offColor: self.getOffColor(), subtitle: "Rating 11 Subtitle", controlState: .readOnly, errorMessage: self.getErrorMessage()) - RatingControlFormView(title: "Rating 12 (Highlighted)", rating: self.$rating12, onImage: self.getOnImage(), offImage: self.getOffImage(), onColor: self.getOnColor(.highlighted), offColor: self.getOffColor(), subtitle: "Rating 12 Subtitle", controlState: .highlighted, errorMessage: self.getErrorMessage()) + RatingControlFormView(title: "Rating 13", rating: self.$rating13, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, subtitle: "Rating 13 Subtitle") + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 14 (Disabled)", rating: self.$rating14, ratingControlStyle: .editableDisabled, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, subtitle: "Rating 14 Subtitle") + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 15 (Read Only)", rating: self.$rating15, ratingControlStyle: .standard, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, subtitle: "Rating 15 Subtitle") + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 16 (Read Only Large)", rating: self.$rating16, ratingControlStyle: .standardLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, subtitle: "Rating 16 Subtitle") + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 17 (Accented)", rating: self.$rating17, ratingControlStyle: .accented, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, subtitle: "Rating 17 Subtitle") + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 18 (Accented Large)", rating: self.$rating18, ratingControlStyle: .accentedLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel, subtitle: "Rating 18 Subtitle") + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + + Text("Customized") + RatingControlFormView(title: "Rating 19", onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating19, itemSize: CGSize(width: 40, height: 40), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 20 (Disabled)", onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating20, ratingControlStyle: .editableDisabled, itemSize: CGSize(width: 30, height: 30), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 21 (Read Only) Rating 21 (Read Only)", onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating21, ratingControlStyle: .standard, itemSize: CGSize(width: 10, height: 10), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 22 (Read Only Large) Rating 22 (Read Only Large)", onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating22, ratingControlStyle: .standardLarge, itemSize: CGSize(width: 20, height: 20), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 23 (Accented)", onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating23, ratingControlStyle: .accented, itemSize: CGSize(width: 5, height: 5), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) + RatingControlFormView(title: "Rating 24 (Accented Large)", onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating24, ratingControlStyle: .accentedLarge, itemSize: CGSize(width: 10, height: 10), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .informationView(isPresented: self.$showsHintMessage, description: AttributedString("hint message")) } } - func getErrorMessage() -> AttributedString? { - self.showsErrorMessage ? self.errorMessage : nil - } - - func getOnColor(_ controlState: ControlState) -> Color? { - guard self.usesCustomColors else { + func getAverageRatingValue() -> CGFloat? { + guard self.setsAverageRating else { return nil } - switch controlState { - case .disabled: - return .orange - case .readOnly: - return .brown - case .highlighted: - return .black - default: - return .red - } - } - - func getOffColor() -> Color? { - self.usesCustomColors ? .yellow : nil + return 2.6 } - func getOnImage() -> Image? { - self.usesCustomImages ? Image(systemName: "hand.thumbsup.fill") : nil + func getReviewCount() -> Int? { + guard self.setsReviewCount else { + return nil + } + return 1234 } - func getOffImage() -> Image? { - self.usesCustomImages ? Image(systemName: "hand.thumbsdown.fill") : nil + func getReviewCountCeiling() -> Int? { + guard self.setsReviewCountCeiling else { + return nil + } + return 1000 } } diff --git a/Apps/Examples/Examples/FioriSwiftUICore/RatingControl/RatingControlExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/RatingControl/RatingControlExample.swift index 84d20d253..9fff27102 100644 --- a/Apps/Examples/Examples/FioriSwiftUICore/RatingControl/RatingControlExample.swift +++ b/Apps/Examples/Examples/FioriSwiftUICore/RatingControl/RatingControlExample.swift @@ -42,38 +42,216 @@ struct RatingControlExample: View { @State var rating20: Int = 4 + @State var rating21: Int = 1 + + @State var rating22: Int = 2 + + @State var rating23: Int = 3 + + @State var rating24: Int = 4 + + @State var showsValueLabel = false + @State var showsReviewCountLabel = false + @State var setsAverageRating = false + @State var setsReviewCount = false + @State var setsReviewCountCeiling = false + var body: some View { List { - Text("RatingControl Default Example") - RatingControl(rating: self.$rating1, ratingControlStyle: .editable) - RatingControl(rating: self.$rating2, ratingControlStyle: .editableDisabled) - RatingControl(rating: self.$rating3, ratingControlStyle: .standard) - RatingControl(rating: self.$rating4, ratingControlStyle: .accented) - - Text("Custom Color Example") - RatingControl(rating: self.$rating5, ratingControlStyle: .editable, onColor: .red, offColor: .yellow) - RatingControl(rating: self.$rating6, ratingControlStyle: .editableDisabled, onColor: .orange, offColor: .yellow) - RatingControl(rating: self.$rating7, ratingControlStyle: .standard, onColor: .brown, offColor: .yellow) - RatingControl(rating: self.$rating8, ratingControlStyle: .accented, onColor: .black, offColor: .yellow) - - Text("Larger Size Example") - RatingControl(rating: self.$rating9, ratingControlStyle: .editable, itemSize: CGSize(width: 50, height: 50)) - RatingControl(rating: self.$rating10, ratingControlStyle: .editableDisabled, itemSize: CGSize(width: 40, height: 40)) - RatingControl(rating: self.$rating11, ratingControlStyle: .standard, itemSize: CGSize(width: 10, height: 10)) - RatingControl(rating: self.$rating12, ratingControlStyle: .accented, itemSize: CGSize(width: 5, height: 5)) - - Text("Custom Image Example") - RatingControl(rating: self.$rating13, ratingControlStyle: .editable, onImage: Image(systemName: "hand.thumbsup.fill"), offImage: Image(systemName: "hand.thumbsdown.fill")) - RatingControl(rating: self.$rating14, ratingControlStyle: .editableDisabled, onImage: Image(systemName: "hand.thumbsup.fill"), offImage: Image(systemName: "hand.thumbsdown.fill")) - RatingControl(rating: self.$rating15, ratingControlStyle: .standard, onImage: Image(systemName: "hand.thumbsup.fill"), offImage: Image(systemName: "hand.thumbsdown.fill")) - RatingControl(rating: self.$rating16, ratingControlStyle: .accented, onImage: Image(systemName: "hand.thumbsup.fill"), offImage: Image(systemName: "hand.thumbsdown.fill")) - - Text("Custom Number of Stars Example") - RatingControl(rating: self.$rating17, ratingControlStyle: .editable, ratingBounds: -5 ... 5) - RatingControl(rating: self.$rating18, ratingControlStyle: .editableDisabled, ratingBounds: -5 ... 5) - RatingControl(rating: self.$rating19, ratingControlStyle: .standard, ratingBounds: -5 ... 5) - RatingControl(rating: self.$rating20, ratingControlStyle: .accented, ratingBounds: -5 ... 5) + Text("RatingControl Examples") + Toggle("Shows Value Label", isOn: self.$showsValueLabel) + .padding(.leading, 16) + .padding(.trailing, 16) + Toggle("Shows Review Count Label", isOn: self.$showsReviewCountLabel) + .padding(.leading, 16) + .padding(.trailing, 16) + Toggle("Sets Average Rating", isOn: self.$setsAverageRating) + .padding(.leading, 16) + .padding(.trailing, 16) + Toggle("Sets Review Count", isOn: self.$setsReviewCount) + .padding(.leading, 16) + .padding(.trailing, 16) + Toggle("Sets Review Count Ceiling", isOn: self.$setsReviewCountCeiling) + .padding(.leading, 16) + .padding(.trailing, 16) + + Text("RatingControl Default Examples") + RatingControl(rating: self.$rating1, ratingControlStyle: .editable, showsValueLabel: self.showsValueLabel) + RatingControl(rating: self.$rating2, ratingControlStyle: .editableDisabled, showsValueLabel: self.showsValueLabel) + RatingControl(rating: self.$rating3, ratingControlStyle: .standard, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(rating: self.$rating4, ratingControlStyle: .standardLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(rating: self.$rating5, ratingControlStyle: .accented, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(rating: self.$rating6, ratingControlStyle: .accentedLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + + Text("Custom Color and Font Examples") + RatingControl(rating: self.$rating7, ratingControlStyle: .editable, showsValueLabel: self.showsValueLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + RatingControl(rating: self.$rating8, ratingControlStyle: .editableDisabled, showsValueLabel: self.showsValueLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + RatingControl(rating: self.$rating9, ratingControlStyle: .standard, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .halfStarImageStyle { style in + HalfStarImage(style) + .foregroundStyle(.green) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + RatingControl(rating: self.$rating10, ratingControlStyle: .standardLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .halfStarImageStyle { style in + HalfStarImage(style) + .foregroundStyle(.green) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + RatingControl(rating: self.$rating11, ratingControlStyle: .accented, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { style in + OnStarImage(style) + .foregroundStyle(.red) + } + .offStarImageStyle { style in + OffStarImage(style) + .foregroundStyle(.yellow) + } + .halfStarImageStyle { style in + HalfStarImage(style) + .foregroundStyle(.green) + } + .valueLabelStyle { style in + ValueLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { style in + ReviewCountLabel(style) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + RatingControl(rating: self.$rating12, ratingControlStyle: .accentedLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + .onStarImageStyle { + OnStarImage($0) + .foregroundStyle(.red) + } + .offStarImageStyle { config in + OffStarImage(config) + .foregroundStyle(.yellow) + } + .halfStarImageStyle { config in + HalfStarImage(config) + .foregroundStyle(.green) + } + .valueLabelStyle { config in + ValueLabel(config) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + .reviewCountLabelStyle { config in + ReviewCountLabel(config) + .font(.fiori(forTextStyle: .headline)) + .foregroundStyle(.brown) + } + + Text("Custom Size Examples") + RatingControl(rating: self.$rating13, ratingControlStyle: .editable, itemSize: CGSize(width: 40, height: 40), showsValueLabel: self.showsValueLabel) + RatingControl(rating: self.$rating14, ratingControlStyle: .editableDisabled, itemSize: CGSize(width: 30, height: 30), showsValueLabel: self.showsValueLabel) + RatingControl(rating: self.$rating15, ratingControlStyle: .standard, itemSize: CGSize(width: 10, height: 10), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(rating: self.$rating16, ratingControlStyle: .standardLarge, itemSize: CGSize(width: 20, height: 20), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(rating: self.$rating17, ratingControlStyle: .accented, itemSize: CGSize(width: 5, height: 5), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(rating: self.$rating18, ratingControlStyle: .accentedLarge, itemSize: CGSize(width: 10, height: 10), showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + + Text("Custom Image Examples") + RatingControl(onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating19, ratingControlStyle: .editable, showsValueLabel: self.showsValueLabel) + RatingControl(onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating20, ratingControlStyle: .editableDisabled, showsValueLabel: self.showsValueLabel) + RatingControl(onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating21, ratingControlStyle: .standard, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating22, ratingControlStyle: .standardLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating23, ratingControlStyle: .accented, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + RatingControl(onStarImage: Image(systemName: "hand.thumbsup.fill").renderingMode(.template).resizable(), offStarImage: Image(systemName: "hand.thumbsdown.fill").renderingMode(.template).resizable(), halfStarImage: Image(systemName: "hand.thumbsup.circle").renderingMode(.template).resizable(), rating: self.$rating24, ratingControlStyle: .accentedLarge, showsValueLabel: self.showsValueLabel, averageRating: self.getAverageRatingValue(), reviewCount: self.getReviewCount(), reviewCountCeiling: self.getReviewCountCeiling(), showsReviewCountLabel: self.showsReviewCountLabel) + } + } + + func getAverageRatingValue() -> CGFloat? { + guard self.setsAverageRating else { + return nil + } + return 2.6 + } + + func getReviewCount() -> Int? { + guard self.setsReviewCount else { + return nil + } + return 1234 + } + + func getReviewCountCeiling() -> Int? { + guard self.setsReviewCountCeiling else { + return nil } + return 1000 } } diff --git a/Sources/FioriSwiftUICore/DataTypes/RatingControl+DataType.swift b/Sources/FioriSwiftUICore/DataTypes/RatingControl+DataType.swift index 71b81ae2e..5990d4a58 100644 --- a/Sources/FioriSwiftUICore/DataTypes/RatingControl+DataType.swift +++ b/Sources/FioriSwiftUICore/DataTypes/RatingControl+DataType.swift @@ -1,14 +1,15 @@ +import FioriThemeManager import SwiftUI public extension RatingControl { /** - The available styles for the `FUIRatingControl`. + The available styles for the `RatingControl`. */ enum Style { /** Editable style. - Each rating star is a SF Symbol body light style (large scale) with tint color. + Each rating star is large scale with tint color. This is the default style. */ case editable @@ -16,23 +17,37 @@ public extension RatingControl { /** Disabled editable style. - Each rating star is the same as `Editable` style but with grey color. + Each rating star is the same as `Editable` style but with grey color. User interaction is disabled. */ case editableDisabled /** Standard style. - This `FUIRatingControl` is read-only. Each rating star is a SF Symbol body light style (small scale). + This `RatingControl` is read-only. Each rating star is in small scale. */ case standard + /** + Standard large style. + + This `RatingControl` is read-only. Each rating star is in large scale. + */ + case standardLarge + /** Accented read-only style. - This `FUIRatingControl` is read-only with accented color. Each rating star is the same as in `standard` style. + This `RatingControl` is read-only. Each rating star is in small scale with accented color. */ case accented + + /** + Accented read-only style. + + This `RatingControl` is read-only. Each rating star is in large scale with accented color. + */ + case accentedLarge } internal static func getAccessibilityLabelString(_ rating: Int, ratingBounds: ClosedRange) -> String { @@ -45,6 +60,7 @@ extension RatingControlConfiguration { struct RatingItem: Identifiable { public let id = UUID() let isOn: Bool + let isHalf: Bool } func ratingItems(_ rating: Int) -> [RatingItem] { @@ -53,80 +69,78 @@ extension RatingControlConfiguration { guard i != self.ratingBounds.upperBound else { continue } - items.append(RatingItem(isOn: i < rating)) + items.append(RatingItem(isOn: i < rating, isHalf: false)) } return items } - func getOnColor() -> Color { - if let onColor { - return onColor + func ratingItems(_ averageRating: CGFloat) -> [RatingItem] { + var items: [RatingItem] = [] + for i in self.ratingBounds { + guard i != self.ratingBounds.upperBound else { + continue + } + let diff = averageRating - CGFloat(i) + if diff < 0.25 { + items.append(RatingItem(isOn: false, isHalf: false)) + } else if diff < 0.75 { + items.append(RatingItem(isOn: false, isHalf: true)) + } else { + items.append(RatingItem(isOn: true, isHalf: false)) + } } + return items + } + + func getOnColor() -> Color { switch self.ratingControlStyle { case .editable: return .preferredColor(.tintColor) case .editableDisabled: return .preferredColor(.quaternaryLabel) - case .standard: + case .standard, .standardLarge: return .preferredColor(.tertiaryLabel) - case .accented: - return .preferredColor(.mango3) + case .accented, .accentedLarge: + return .preferredColor(.mango8) } } func getOffColor() -> Color { - if let offColor { - return offColor - } switch self.ratingControlStyle { case .editable: return .preferredColor(.tintColor) case .editableDisabled: return .preferredColor(.quaternaryLabel) - case .standard: + case .standard, .standardLarge: return .preferredColor(.tertiaryLabel) - case .accented: - return .preferredColor(.mango4) + case .accented, .accentedLarge: + return .preferredColor(.mango8) } } - func getOnImageView() -> some View { - self.getOnImage() - .resizable() - .scaledToFit() - .frame(width: self.getItemSize().width, height: self.getItemSize().height) - .font(.body) - .fontWeight(.light) - .imageScale(self.getScale()) - .foregroundColor(self.getOnColor()) - } - - func getOffImageView() -> some View { - self.getOffImage() - .resizable() - .scaledToFit() - .frame(width: self.getItemSize().width, height: self.getItemSize().height) - .font(.body) - .fontWeight(.light) - .imageScale(self.getScale()) - .foregroundColor(self.getOffColor()) - } - - func getOnImage() -> Image { - let image: Image = (onImage ?? Image(systemName: "star.fill")) - .renderingMode(.template) - return image + func getDefaultLabelFont() -> Font { + switch ratingControlStyle { + case .editable, .editableDisabled, .standardLarge, .accentedLarge: + return .fiori(forTextStyle: .body) + case .standard, .accented: + return .fiori(forTextStyle: .subheadline) + } } - func getOffImage() -> Image { - let image: Image = (offImage ?? Image(systemName: "star")) - .renderingMode(.template) - return image + func getDefaultLabelColor() -> Color { + switch ratingControlStyle { + case .editable: + return .preferredColor(.primaryLabel) + case .editableDisabled: + return .preferredColor(.quaternaryLabel) + case .standard, .standardLarge, .accented, .accentedLarge: + return .preferredColor(.tertiaryLabel) + } } func getScale() -> Image.Scale { switch self.ratingControlStyle { - case .editable, .editableDisabled: + case .editable, .editableDisabled, .standardLarge, .accentedLarge: return .large case .standard, .accented: return .small @@ -138,7 +152,7 @@ extension RatingControlConfiguration { return itemSize } switch self.ratingControlStyle { - case .editable, .editableDisabled: + case .editable, .editableDisabled, .standardLarge, .accentedLarge: return CGSize(width: 28, height: 28) case .standard, .accented: return CGSize(width: 16, height: 16) @@ -150,10 +164,10 @@ extension RatingControlConfiguration { return interItemSpacing } switch self.ratingControlStyle { - case .editable, .editableDisabled: - return CGFloat(4) + case .editable, .editableDisabled, .standardLarge, .accentedLarge: + return CGFloat(8) case .standard, .accented: - return CGFloat(2) + return CGFloat(6) } } diff --git a/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift b/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift index 928af4edb..37d475344 100755 --- a/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift +++ b/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift @@ -339,3 +339,36 @@ protocol _AvatarsTitleComponent { // sourcery: @ViewBuilder var avatarsTitle: AttributedString? { get } } + +// sourcery: BaseComponent +protocol _ReviewCountLabelComponent { + // sourcery: @ViewBuilder + var reviewCountLabel: AttributedString? { get } +} + +// sourcery: BaseComponent +// sourcery: importFrameworks = ["FioriThemeManager"] +protocol _OnStarImageComponent { + /// The image to be used for "On" rating star. + // sourcery: @ViewBuilder + // sourcery: defaultValue = "FioriIcon.actions.favorite.renderingMode(.template).resizable()" + var onStarImage: Image { get } +} + +// sourcery: BaseComponent +// sourcery: importFrameworks = ["FioriThemeManager"] +protocol _OffStarImageComponent { + //// The image to be used for "Off" rating star. + // sourcery: @ViewBuilder + // sourcery: defaultValue = "FioriIcon.actions.unfavorite.renderingMode(.template).resizable()" + var offStarImage: Image { get } +} + +// sourcery: BaseComponent +// sourcery: importFrameworks = ["FioriThemeManager"] +protocol _HalfStarImageComponent { + //// The image to be used for "half" rating star. + // sourcery: @ViewBuilder + // sourcery: defaultValue = "FioriIcon.actions.halfStar.renderingMode(.template).resizable()" + var halfStarImage: Image { get } +} diff --git a/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift b/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift index f3cd209f0..e3c1cef86 100755 --- a/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift +++ b/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift @@ -295,7 +295,8 @@ protocol _BannerMessageComponent: _IconComponent, _TitleComponent, _CloseActionC /// The default "On" image is a filled star while the default "Off" inmage /// is an unfilled star. // sourcery: CompositeComponent -protocol _RatingControlComponent { +// sourcery: importFrameworks = ["FioriThemeManager"] +protocol _RatingControlComponent: _ValueLabelComponent, _OnStarImageComponent, _OffStarImageComponent, _HalfStarImageComponent, _ReviewCountLabelComponent { // sourcery: @Binding /// The rating value. var rating: Int { get } @@ -308,23 +309,48 @@ protocol _RatingControlComponent { // sourcery: defaultValue = 0...5 var ratingBounds: ClosedRange { get } - /// The custom image to be used for "On". - var onImage: Image? { get } - - /// The custom image to be used for "Off". - var offImage: Image? { get } - /// The custom fixed size of each item image view. var itemSize: CGSize? { get } - /// The custom color for the ON image. - var onColor: Color? { get } - - /// The custom color for the OFF image. - var offColor: Color? { get } - /// The custom spacing between images. var interItemSpacing: CGFloat? { get } + + /// The rating format for displaying rating value. + /// When this is `nil`, the default format is "%d of %d" where "of" is the localized "of". The first parameter is the rating value while the second parameter is the total number of stars. + // sourcery: defaultValue = "nil" + var ratingValueFormat: String? { get } + + /// This property indicates if the value label is to be displayed or not. The default value is `false` for backward compatibility. + // sourcery: defaultValue = "false" + var showsValueLabel: Bool { get } + + /// The average rating for read-only style. + // sourcery: defaultValue = "nil" + var averageRating: CGFloat? { get } + + /// The format for display the average rating. The default is "%.1f" + // sourcery: defaultValue = ""%.1f"" + var averageRatingFormat: String { get } + + /// The number of reviews. + // sourcery: defaultValue = "nil" + var reviewCount: Int? { get } + + /// The format for the review count string. The default is "%d reviews" where "reviews" is the localized "reviews" string. + // sourcery: defaultValue = "nil" + var reviewCountFormat: String? { get } + + /// The ceiling number to be displayed for review count. If the `reviewCount` is larger than this number, this number will be displayed with a "+" sign after the number. + // sourcery: defaultValue = "nil" + var reviewCountCeiling: Int? { get } + + /// The format for the review count string when the count is over the ceiling. The default is "%d+ reviews" where "reviews" is the localized "reviews" string. + // sourcery: defaultValue = "nil" + var reviewCountCeilingFormat: String? { get } + + /// This property indicates if the review count label is to be displayed or not. The default value is `false` for backward compatibility. + // sourcery: defaultValue = "false" + var showsReviewCountLabel: Bool { get } } /// `TimelineMarker` is a non-selectable view intended for timelineMarkers that require beforeStart, start, beforeEnd and end status that displays timelineMarker details. @@ -392,7 +418,8 @@ protocol _TimelineNowIndicatorComponent: _NowIndicatorNodeComponent {} /// The form view which contains a title, rating control, and a subtitle // sourcery: CompositeComponent -protocol _RatingControlFormViewComponent: _TitleComponent, _RatingControlComponent, _SubtitleComponent, _FormViewComponent { +// sourcery: importFrameworks = ["FioriThemeManager"] +protocol _RatingControlFormViewComponent: _TitleComponent, _RatingControlComponent, _SubtitleComponent { /// Indicates if the axis for displaying the title and rating control. // sourcery: defaultValue = .horizontal var axis: Axis { get } diff --git a/Sources/FioriSwiftUICore/_FioriStyles/HalfStarImageStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/HalfStarImageStyle.fiori.swift new file mode 100644 index 000000000..c541db53b --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/HalfStarImageStyle.fiori.swift @@ -0,0 +1,20 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct HalfStarImageBaseStyle: HalfStarImageStyle { + @ViewBuilder + public func makeBody(_ configuration: HalfStarImageConfiguration) -> some View { + // Add default layout here + configuration.halfStarImage + } +} + +// Default fiori styles +public struct HalfStarImageFioriStyle: HalfStarImageStyle { + @ViewBuilder + public func makeBody(_ configuration: HalfStarImageConfiguration) -> some View { + HalfStarImage(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/OffStarImageStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/OffStarImageStyle.fiori.swift new file mode 100644 index 000000000..c00a5bd5f --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/OffStarImageStyle.fiori.swift @@ -0,0 +1,20 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct OffStarImageBaseStyle: OffStarImageStyle { + @ViewBuilder + public func makeBody(_ configuration: OffStarImageConfiguration) -> some View { + // Add default layout here + configuration.offStarImage + } +} + +// Default fiori styles +public struct OffStarImageFioriStyle: OffStarImageStyle { + @ViewBuilder + public func makeBody(_ configuration: OffStarImageConfiguration) -> some View { + OffStarImage(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/OnStarImageStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/OnStarImageStyle.fiori.swift new file mode 100644 index 000000000..77a95db5b --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/OnStarImageStyle.fiori.swift @@ -0,0 +1,20 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct OnStarImageBaseStyle: OnStarImageStyle { + @ViewBuilder + public func makeBody(_ configuration: OnStarImageConfiguration) -> some View { + // Add default layout here + configuration.onStarImage + } +} + +// Default fiori styles +public struct OnStarImageFioriStyle: OnStarImageStyle { + @ViewBuilder + public func makeBody(_ configuration: OnStarImageConfiguration) -> some View { + OnStarImage(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/RatingControlFormViewStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/RatingControlFormViewStyle.fiori.swift index 5fa011e9c..a9a9b28a8 100644 --- a/Sources/FioriSwiftUICore/_FioriStyles/RatingControlFormViewStyle.fiori.swift +++ b/Sources/FioriSwiftUICore/_FioriStyles/RatingControlFormViewStyle.fiori.swift @@ -9,7 +9,7 @@ public struct RatingControlFormViewBaseStyle: RatingControlFormViewStyle { VStack { if !configuration.subtitle.isEmpty { VStack(spacing: 4.0) { - self.getRatingControl(configuration) + configuration._ratingControl .frame(maxWidth: .infinity) .frame(alignment: .center) configuration.subtitle @@ -24,23 +24,22 @@ public struct RatingControlFormViewBaseStyle: RatingControlFormViewStyle { } HStack { Spacer() - self.getRatingControl(configuration) + configuration._ratingControl } } } else { HStack { configuration.title Spacer() - self.getRatingControl(configuration) + configuration._ratingControl + .layoutPriority(2) } } } - .informationView(isPresented: Binding(get: { self.isInfoViewNeeded(configuration) }, set: { _ in }), description: configuration.errorMessage ?? "") - .informationViewStyle(.error) - .padding(.bottom, self.isInfoViewNeeded(configuration) ? -11.0 : 0) + .padding(.bottom, 0) .accessibilityElement(children: .combine) - .setAccessibilityAdjustable(self.getRatingControl(configuration).ratingControlStyle == .editable) { direction in - if self.getRatingControl(configuration).ratingControlStyle == .editable { + .setAccessibilityAdjustable(configuration.ratingControlStyle == .editable) { direction in + if configuration.ratingControlStyle == .editable { switch direction { case .increment: let incrementValue = configuration.rating + 1 @@ -57,30 +56,6 @@ public struct RatingControlFormViewBaseStyle: RatingControlFormViewStyle { } } - func getRatingControl(_ configuration: RatingControlFormViewConfiguration) -> RatingControl { - // the rating control style is based on the controlState - let ratingControlStyle: RatingControl.Style - switch configuration.controlState { - case .disabled: - ratingControlStyle = .editableDisabled - case .readOnly: - ratingControlStyle = .standard - case .highlighted: - ratingControlStyle = .accented - default: - ratingControlStyle = .editable - } - - return RatingControl(rating: configuration.$rating, ratingControlStyle: ratingControlStyle, ratingBounds: configuration.ratingBounds, onImage: configuration.onImage, offImage: configuration.offImage, itemSize: configuration.itemSize, onColor: configuration.onColor, offColor: configuration.offColor, interItemSpacing: configuration.interItemSpacing) - } - - func isInfoViewNeeded(_ configuration: RatingControlFormViewConfiguration) -> Bool { - if let errorMessage = configuration.errorMessage, !errorMessage.characters.isEmpty { - return true - } - return false - } - func setRatingValue(_ configuration: RatingControlFormViewConfiguration, newRating: Int) { if configuration.rating != newRating { configuration.rating = newRating @@ -107,8 +82,8 @@ extension RatingControlFormViewFioriStyle { } func getTitleColor() -> Color { - switch self.ratingControlFormViewConfiguration.controlState { - case .disabled: + switch self.ratingControlFormViewConfiguration.ratingControlStyle { + case .editableDisabled: return .preferredColor(.separator) default: return .preferredColor(.primaryLabel) @@ -116,6 +91,46 @@ extension RatingControlFormViewFioriStyle { } } + struct ValueLabelFioriStyle: ValueLabelStyle { + let ratingControlFormViewConfiguration: RatingControlFormViewConfiguration + + func makeBody(_ configuration: ValueLabelConfiguration) -> some View { + ValueLabel(configuration) + } + } + + struct OnStarImageFioriStyle: OnStarImageStyle { + let ratingControlFormViewConfiguration: RatingControlFormViewConfiguration + + func makeBody(_ configuration: OnStarImageConfiguration) -> some View { + OnStarImage(configuration) + } + } + + struct OffStarImageFioriStyle: OffStarImageStyle { + let ratingControlFormViewConfiguration: RatingControlFormViewConfiguration + + func makeBody(_ configuration: OffStarImageConfiguration) -> some View { + OffStarImage(configuration) + } + } + + struct HalfStarImageFioriStyle: HalfStarImageStyle { + let ratingControlFormViewConfiguration: RatingControlFormViewConfiguration + + func makeBody(_ configuration: HalfStarImageConfiguration) -> some View { + HalfStarImage(configuration) + } + } + + struct ReviewCountLabelFioriStyle: ReviewCountLabelStyle { + let ratingControlFormViewConfiguration: RatingControlFormViewConfiguration + + func makeBody(_ configuration: ReviewCountLabelConfiguration) -> some View { + ReviewCountLabel(configuration) + } + } + struct SubtitleFioriStyle: SubtitleStyle { let ratingControlFormViewConfiguration: RatingControlFormViewConfiguration @@ -126,8 +141,8 @@ extension RatingControlFormViewFioriStyle { } func getSubtitleColor() -> Color { - switch self.ratingControlFormViewConfiguration.controlState { - case .disabled: + switch self.ratingControlFormViewConfiguration.ratingControlStyle { + case .editableDisabled: return .preferredColor(.separator) default: return .preferredColor(.tertiaryLabel) @@ -142,12 +157,4 @@ extension RatingControlFormViewFioriStyle { RatingControl(configuration) } } - - struct FormViewFioriStyle: FormViewStyle { - let ratingControlFormViewConfiguration: RatingControlFormViewConfiguration - - func makeBody(_ configuration: FormViewConfiguration) -> some View { - FormView(configuration) - } - } } diff --git a/Sources/FioriSwiftUICore/_FioriStyles/RatingControlStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/RatingControlStyle.fiori.swift index 272bded4f..7bd546859 100644 --- a/Sources/FioriSwiftUICore/_FioriStyles/RatingControlStyle.fiori.swift +++ b/Sources/FioriSwiftUICore/_FioriStyles/RatingControlStyle.fiori.swift @@ -4,51 +4,107 @@ import SwiftUI // Base Layout style public struct RatingControlBaseStyle: RatingControlStyle { + @Environment(\.sizeCategory) var sizeCategory + public func makeBody(_ configuration: RatingControlConfiguration) -> some View { - HStack(spacing: configuration.getItemSpacing()) { - ForEach(configuration.ratingItems(configuration.rating)) { ratingItem in - if ratingItem.isOn { - configuration.getOnImageView() - } else { - configuration.getOffImageView() + self.getMainBody(configuration) + .onTapGesture { location in + if configuration.ratingControlStyle == .editable { + self.setRatingValue(configuration, location: location, isTap: true) } } - } - .onTapGesture { location in - if configuration.ratingControlStyle == .editable { - self.setRatingValue(configuration, location: location) + .gesture( + DragGesture(minimumDistance: 0.5) + .onChanged { value in + if configuration.ratingControlStyle == .editable { + self.setRatingValue(configuration, location: value.location) + } + } + ) + .accessibilityElement() + .accessibilityLabel(self.getAccessibilityLabel(configuration)) + .setAccessibilityAdjustable(configuration.ratingControlStyle == .editable) { direction in + if configuration.ratingControlStyle == .editable { + switch direction { + case .increment: + let incrementValue = configuration.rating + 1 + let newRating = min(configuration.ratingBounds.upperBound, incrementValue) + self.setRatingValue(configuration, newRating: newRating) + case .decrement: + let decrementValue = configuration.rating - 1 + let newRating = max(configuration.ratingBounds.lowerBound, decrementValue) + self.setRatingValue(configuration, newRating: newRating) + @unknown default: + break + } + } + } + } + + @ViewBuilder func getMainBody(_ configuration: RatingControlConfiguration) -> some View { + if self.sizeCategory >= ContentSizeCategory.extraExtraExtraLarge { + VStack { + self.getFirstLabel(configuration) + HStack(spacing: configuration.getItemSpacing()) { + self.getRatingView(configuration) + } + self.getSecondLabel(configuration) + } + } else { + HStack(spacing: configuration.getItemSpacing()) { + self.getFirstLabel(configuration) + self.getRatingView(configuration) + self.getSecondLabel(configuration) } } - .gesture( - DragGesture(minimumDistance: 0.5) - .onChanged { value in - if configuration.ratingControlStyle == .editable { - self.setRatingValue(configuration, location: value.location) - } + } + + @ViewBuilder func getFirstLabel(_ configuration: RatingControlConfiguration) -> some View { + if configuration.showsValueLabel, self.getReadOnly(configuration) { + // shows value label + ValueLabel(valueLabel: AttributedString(self.getValueText(configuration))) + } + } + + @ViewBuilder func getRatingView(_ configuration: RatingControlConfiguration) -> some View { + if self.getReadOnly(configuration), let averageRating = configuration.averageRating { + ForEach(configuration.ratingItems(averageRating)) { ratingItem in + if ratingItem.isOn { + configuration.onStarImage + } else if ratingItem.isHalf { + configuration.halfStarImage + } else { + configuration.offStarImage } - ) - .accessibilityElement() - .accessibilityLabel(self.getAccessibilityLabel(configuration)) - .setAccessibilityAdjustable(configuration.ratingControlStyle == .editable) { direction in - if configuration.ratingControlStyle == .editable { - switch direction { - case .increment: - let incrementValue = configuration.rating + 1 - let newRating = min(configuration.ratingBounds.upperBound, incrementValue) - self.setRatingValue(configuration, newRating: newRating) - case .decrement: - let decrementValue = configuration.rating - 1 - let newRating = max(configuration.ratingBounds.lowerBound, decrementValue) - self.setRatingValue(configuration, newRating: newRating) - @unknown default: - break + } + } else { + ForEach(configuration.ratingItems(configuration.rating)) { ratingItem in + if ratingItem.isOn { + configuration.onStarImage + } else { + configuration.offStarImage } } } } - func setRatingValue(_ configuration: RatingControlConfiguration, location: CGPoint) { - let newValue = configuration.getRatingValue(location) + @ViewBuilder func getSecondLabel(_ configuration: RatingControlConfiguration) -> some View { + if configuration.showsValueLabel, !self.getReadOnly(configuration) { + // shows value label + ValueLabel(valueLabel: AttributedString(self.getValueText(configuration))) + } + if let reviewCount = configuration.reviewCount, configuration.showsReviewCountLabel, getReadOnly(configuration) { + // shows review count + ReviewCountLabel(reviewCountLabel: AttributedString(self.getReviewCountText(configuration, reviewCount: reviewCount))) + } + } + + func setRatingValue(_ configuration: RatingControlConfiguration, location: CGPoint, isTap: Bool = false) { + var newValue = configuration.getRatingValue(location) + if isTap, newValue == 1, configuration.rating == 1 { + // Tap on the first star is set rating to 0 if original rating is 1 + newValue = 0 + } self.setRatingValue(configuration, newRating: newValue) } @@ -62,6 +118,33 @@ public struct RatingControlBaseStyle: RatingControlStyle { func getAccessibilityLabel(_ configuration: RatingControlConfiguration) -> String { RatingControl.getAccessibilityLabelString(configuration.rating, ratingBounds: configuration.ratingBounds) } + + func getReadOnly(_ configuration: RatingControlConfiguration) -> Bool { + switch configuration.ratingControlStyle { + case .editable, .editableDisabled: + return false + default: + return true + } + } + + func getValueText(_ configuration: RatingControlConfiguration) -> String { + guard self.getReadOnly(configuration) else { + let format = configuration.ratingValueFormat ?? NSLocalizedString("%d of %d", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "") + return String(format: format, configuration.rating, configuration.ratingBounds.upperBound) + } + let avgRating = configuration.averageRating ?? CGFloat(configuration.rating) + return String(format: configuration.averageRatingFormat, avgRating, configuration.ratingBounds.upperBound) + } + + func getReviewCountText(_ configuration: RatingControlConfiguration, reviewCount: Int) -> String { + if let reviewCountCeiling = configuration.reviewCountCeiling, reviewCount > reviewCountCeiling { + let format = configuration.reviewCountCeilingFormat ?? NSLocalizedString("%d+ reviews", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "") + return String(format: format, reviewCountCeiling) + } + let format = configuration.reviewCountFormat ?? NSLocalizedString("%d reviews", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "") + return String(format: format, reviewCount) + } } // Default fiori styles @@ -71,6 +154,68 @@ extension RatingControlFioriStyle { RatingControl(configuration) } } + + struct ValueLabelFioriStyle: ValueLabelStyle { + let ratingControlConfiguration: RatingControlConfiguration + + func makeBody(_ configuration: ValueLabelConfiguration) -> some View { + ValueLabel(configuration) + .font(self.ratingControlConfiguration.getDefaultLabelFont()) + .foregroundStyle(self.ratingControlConfiguration.getDefaultLabelColor()) + } + } + + struct OnStarImageFioriStyle: OnStarImageStyle { + let ratingControlConfiguration: RatingControlConfiguration + + func makeBody(_ configuration: OnStarImageConfiguration) -> some View { + OnStarImage(configuration) + .scaledToFit() + .frame(width: self.ratingControlConfiguration.getItemSize().width, height: self.ratingControlConfiguration.getItemSize().height) + .font(.body) + .fontWeight(.light) + .imageScale(self.ratingControlConfiguration.getScale()) + .foregroundColor(self.ratingControlConfiguration.getOnColor()) + } + } + + struct OffStarImageFioriStyle: OffStarImageStyle { + let ratingControlConfiguration: RatingControlConfiguration + + func makeBody(_ configuration: OffStarImageConfiguration) -> some View { + OffStarImage(configuration) + .scaledToFit() + .frame(width: self.ratingControlConfiguration.getItemSize().width, height: self.ratingControlConfiguration.getItemSize().height) + .font(.body) + .fontWeight(.light) + .imageScale(self.ratingControlConfiguration.getScale()) + .foregroundColor(self.ratingControlConfiguration.getOffColor()) + } + } + + struct HalfStarImageFioriStyle: HalfStarImageStyle { + let ratingControlConfiguration: RatingControlConfiguration + + func makeBody(_ configuration: HalfStarImageConfiguration) -> some View { + HalfStarImage(configuration) + .scaledToFit() + .frame(width: self.ratingControlConfiguration.getItemSize().width, height: self.ratingControlConfiguration.getItemSize().height) + .font(.fiori(forTextStyle: .body)) + .fontWeight(.light) + .imageScale(self.ratingControlConfiguration.getScale()) + .foregroundColor(self.ratingControlConfiguration.getOnColor()) + } + } + + struct ReviewCountLabelFioriStyle: ReviewCountLabelStyle { + let ratingControlConfiguration: RatingControlConfiguration + + func makeBody(_ configuration: ReviewCountLabelConfiguration) -> some View { + ReviewCountLabel(configuration) + .font(self.ratingControlConfiguration.getDefaultLabelFont()) + .foregroundStyle(self.ratingControlConfiguration.getDefaultLabelColor()) + } + } } extension View { diff --git a/Sources/FioriSwiftUICore/_FioriStyles/ReviewCountLabelStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/ReviewCountLabelStyle.fiori.swift new file mode 100644 index 000000000..8aa90d66c --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/ReviewCountLabelStyle.fiori.swift @@ -0,0 +1,20 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct ReviewCountLabelBaseStyle: ReviewCountLabelStyle { + @ViewBuilder + public func makeBody(_ configuration: ReviewCountLabelConfiguration) -> some View { + // Add default layout here + configuration.reviewCountLabel + } +} + +// Default fiori styles +public struct ReviewCountLabelFioriStyle: ReviewCountLabelStyle { + @ViewBuilder + public func makeBody(_ configuration: ReviewCountLabelConfiguration) -> some View { + ReviewCountLabel(configuration) + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/HalfStarImage/HalfStarImage.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/HalfStarImage/HalfStarImage.generated.swift new file mode 100644 index 000000000..bf42c7f0b --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/HalfStarImage/HalfStarImage.generated.swift @@ -0,0 +1,66 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +import FioriThemeManager + +public struct HalfStarImage { + /// / The image to be used for "half" rating star. + let halfStarImage: any View + + @Environment(\.halfStarImageStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder halfStarImage: () -> any View) { + self.halfStarImage = halfStarImage() + } +} + +public extension HalfStarImage { + init(halfStarImage: Image = FioriIcon.actions.halfStar.renderingMode(.template).resizable()) { + self.init(halfStarImage: { halfStarImage }) + } +} + +public extension HalfStarImage { + init(_ configuration: HalfStarImageConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: HalfStarImageConfiguration, shouldApplyDefaultStyle: Bool) { + self.halfStarImage = configuration.halfStarImage + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension HalfStarImage: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(halfStarImage: .init(self.halfStarImage))).typeErased + .transformEnvironment(\.halfStarImageStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension HalfStarImage { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + HalfStarImage(.init(halfStarImage: .init(self.halfStarImage))) + .shouldApplyDefaultStyle(false) + .halfStarImageStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/HalfStarImage/HalfStarImageStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/HalfStarImage/HalfStarImageStyle.generated.swift new file mode 100644 index 000000000..2232c4cf3 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/HalfStarImage/HalfStarImageStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol HalfStarImageStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: HalfStarImageConfiguration) -> Body +} + +struct AnyHalfStarImageStyle: HalfStarImageStyle { + let content: (HalfStarImageConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (HalfStarImageConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: HalfStarImageConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct HalfStarImageConfiguration { + public let halfStarImage: HalfStarImage + + public typealias HalfStarImage = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/OffStarImage/OffStarImage.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OffStarImage/OffStarImage.generated.swift new file mode 100644 index 000000000..136ca7547 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OffStarImage/OffStarImage.generated.swift @@ -0,0 +1,66 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +import FioriThemeManager + +public struct OffStarImage { + /// / The image to be used for "Off" rating star. + let offStarImage: any View + + @Environment(\.offStarImageStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder offStarImage: () -> any View) { + self.offStarImage = offStarImage() + } +} + +public extension OffStarImage { + init(offStarImage: Image = FioriIcon.actions.unfavorite.renderingMode(.template).resizable()) { + self.init(offStarImage: { offStarImage }) + } +} + +public extension OffStarImage { + init(_ configuration: OffStarImageConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: OffStarImageConfiguration, shouldApplyDefaultStyle: Bool) { + self.offStarImage = configuration.offStarImage + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension OffStarImage: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(offStarImage: .init(self.offStarImage))).typeErased + .transformEnvironment(\.offStarImageStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension OffStarImage { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + OffStarImage(.init(offStarImage: .init(self.offStarImage))) + .shouldApplyDefaultStyle(false) + .offStarImageStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/OffStarImage/OffStarImageStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OffStarImage/OffStarImageStyle.generated.swift new file mode 100644 index 000000000..c397eddcf --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OffStarImage/OffStarImageStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol OffStarImageStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: OffStarImageConfiguration) -> Body +} + +struct AnyOffStarImageStyle: OffStarImageStyle { + let content: (OffStarImageConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (OffStarImageConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: OffStarImageConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct OffStarImageConfiguration { + public let offStarImage: OffStarImage + + public typealias OffStarImage = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/OnStarImage/OnStarImage.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OnStarImage/OnStarImage.generated.swift new file mode 100644 index 000000000..0aa3cc5b6 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OnStarImage/OnStarImage.generated.swift @@ -0,0 +1,66 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +import FioriThemeManager + +public struct OnStarImage { + /// The image to be used for "On" rating star. + let onStarImage: any View + + @Environment(\.onStarImageStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder onStarImage: () -> any View) { + self.onStarImage = onStarImage() + } +} + +public extension OnStarImage { + init(onStarImage: Image = FioriIcon.actions.favorite.renderingMode(.template).resizable()) { + self.init(onStarImage: { onStarImage }) + } +} + +public extension OnStarImage { + init(_ configuration: OnStarImageConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: OnStarImageConfiguration, shouldApplyDefaultStyle: Bool) { + self.onStarImage = configuration.onStarImage + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension OnStarImage: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(onStarImage: .init(self.onStarImage))).typeErased + .transformEnvironment(\.onStarImageStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension OnStarImage { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + OnStarImage(.init(onStarImage: .init(self.onStarImage))) + .shouldApplyDefaultStyle(false) + .onStarImageStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/OnStarImage/OnStarImageStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OnStarImage/OnStarImageStyle.generated.swift new file mode 100644 index 000000000..6041559a0 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/OnStarImage/OnStarImageStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol OnStarImageStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: OnStarImageConfiguration) -> Body +} + +struct AnyOnStarImageStyle: OnStarImageStyle { + let content: (OnStarImageConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (OnStarImageConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: OnStarImageConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct OnStarImageConfiguration { + public let onStarImage: OnStarImage + + public typealias OnStarImage = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControl.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControl.generated.swift index b89b25b01..17354755a 100644 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControl.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControl.generated.swift @@ -3,54 +3,120 @@ import Foundation import SwiftUI +import FioriThemeManager + /// `RatingControl` uses images to represent a rating. /// /// The number of "On" images denotes the rating. /// The default "On" image is a filled star while the default "Off" inmage /// is an unfilled star. public struct RatingControl { + let valueLabel: any View + /// The image to be used for "On" rating star. + let onStarImage: any View + /// / The image to be used for "Off" rating star. + let offStarImage: any View + /// / The image to be used for "half" rating star. + let halfStarImage: any View + let reviewCountLabel: any View /// The rating value. @Binding var rating: Int /// The style of this `RatingControl`. let ratingControlStyle: RatingControl.Style /// The range of the rating values. The default is `0...5`. let ratingBounds: ClosedRange - /// The custom image to be used for "On". - let onImage: Image? - /// The custom image to be used for "Off". - let offImage: Image? /// The custom fixed size of each item image view. let itemSize: CGSize? - /// The custom color for the ON image. - let onColor: Color? - /// The custom color for the OFF image. - let offColor: Color? /// The custom spacing between images. let interItemSpacing: CGFloat? + /// The rating format for displaying rating value. + /// When this is `nil`, the default format is "%d of %d" where "of" is the localized "of". The first parameter is the rating value while the second parameter is the total number of stars. + let ratingValueFormat: String? + /// This property indicates if the value label is to be displayed or not. The default value is `false` for backward compatibility. + let showsValueLabel: Bool + /// The average rating for read-only style. + let averageRating: CGFloat? + /// The format for display the average rating. The default is "%.1f" + let averageRatingFormat: String + /// The number of reviews. + let reviewCount: Int? + /// The format for the review count string. The default is "%d reviews" where "reviews" is the localized "reviews" string. + let reviewCountFormat: String? + /// The ceiling number to be displayed for review count. If the `reviewCount` is larger than this number, this number will be displayed with a "+" sign after the number. + let reviewCountCeiling: Int? + /// The format for the review count string when the count is over the ceiling. The default is "%d+ reviews" where "reviews" is the localized "reviews" string. + let reviewCountCeilingFormat: String? + /// This property indicates if the review count label is to be displayed or not. The default value is `false` for backward compatibility. + let showsReviewCountLabel: Bool @Environment(\.ratingControlStyle) var style fileprivate var _shouldApplyDefaultStyle = true - public init(rating: Binding, + public init(@ViewBuilder valueLabel: () -> any View = { EmptyView() }, + @ViewBuilder onStarImage: () -> any View, + @ViewBuilder offStarImage: () -> any View, + @ViewBuilder halfStarImage: () -> any View, + @ViewBuilder reviewCountLabel: () -> any View = { EmptyView() }, + rating: Binding, ratingControlStyle: RatingControl.Style = .editable, ratingBounds: ClosedRange = 0 ... 5, - onImage: Image? = nil, - offImage: Image? = nil, itemSize: CGSize? = nil, - onColor: Color? = nil, - offColor: Color? = nil, - interItemSpacing: CGFloat? = nil) + interItemSpacing: CGFloat? = nil, + ratingValueFormat: String? = nil, + showsValueLabel: Bool = false, + averageRating: CGFloat? = nil, + averageRatingFormat: String = "%.1f", + reviewCount: Int? = nil, + reviewCountFormat: String? = nil, + reviewCountCeiling: Int? = nil, + reviewCountCeilingFormat: String? = nil, + showsReviewCountLabel: Bool = false) { + self.valueLabel = ValueLabel(valueLabel: valueLabel) + self.onStarImage = OnStarImage(onStarImage: onStarImage) + self.offStarImage = OffStarImage(offStarImage: offStarImage) + self.halfStarImage = HalfStarImage(halfStarImage: halfStarImage) + self.reviewCountLabel = ReviewCountLabel(reviewCountLabel: reviewCountLabel) self._rating = rating self.ratingControlStyle = ratingControlStyle self.ratingBounds = ratingBounds - self.onImage = onImage - self.offImage = offImage self.itemSize = itemSize - self.onColor = onColor - self.offColor = offColor self.interItemSpacing = interItemSpacing + self.ratingValueFormat = ratingValueFormat + self.showsValueLabel = showsValueLabel + self.averageRating = averageRating + self.averageRatingFormat = averageRatingFormat + self.reviewCount = reviewCount + self.reviewCountFormat = reviewCountFormat + self.reviewCountCeiling = reviewCountCeiling + self.reviewCountCeilingFormat = reviewCountCeilingFormat + self.showsReviewCountLabel = showsReviewCountLabel + } +} + +public extension RatingControl { + init(valueLabel: AttributedString? = nil, + onStarImage: Image = FioriIcon.actions.favorite.renderingMode(.template).resizable(), + offStarImage: Image = FioriIcon.actions.unfavorite.renderingMode(.template).resizable(), + halfStarImage: Image = FioriIcon.actions.halfStar.renderingMode(.template).resizable(), + reviewCountLabel: AttributedString? = nil, + rating: Binding, + ratingControlStyle: RatingControl.Style = .editable, + ratingBounds: ClosedRange = 0 ... 5, + itemSize: CGSize? = nil, + interItemSpacing: CGFloat? = nil, + ratingValueFormat: String? = nil, + showsValueLabel: Bool = false, + averageRating: CGFloat? = nil, + averageRatingFormat: String = "%.1f", + reviewCount: Int? = nil, + reviewCountFormat: String? = nil, + reviewCountCeiling: Int? = nil, + reviewCountCeilingFormat: String? = nil, + showsReviewCountLabel: Bool = false) + { + self.init(valueLabel: { OptionalText(valueLabel) }, onStarImage: { onStarImage }, offStarImage: { offStarImage }, halfStarImage: { halfStarImage }, reviewCountLabel: { OptionalText(reviewCountLabel) }, rating: rating, ratingControlStyle: ratingControlStyle, ratingBounds: ratingBounds, itemSize: itemSize, interItemSpacing: interItemSpacing, ratingValueFormat: ratingValueFormat, showsValueLabel: showsValueLabel, averageRating: averageRating, averageRatingFormat: averageRatingFormat, reviewCount: reviewCount, reviewCountFormat: reviewCountFormat, reviewCountCeiling: reviewCountCeiling, reviewCountCeilingFormat: reviewCountCeilingFormat, showsReviewCountLabel: showsReviewCountLabel) } } @@ -60,15 +126,25 @@ public extension RatingControl { } internal init(_ configuration: RatingControlConfiguration, shouldApplyDefaultStyle: Bool) { + self.valueLabel = configuration.valueLabel + self.onStarImage = configuration.onStarImage + self.offStarImage = configuration.offStarImage + self.halfStarImage = configuration.halfStarImage + self.reviewCountLabel = configuration.reviewCountLabel self._rating = configuration.$rating self.ratingControlStyle = configuration.ratingControlStyle self.ratingBounds = configuration.ratingBounds - self.onImage = configuration.onImage - self.offImage = configuration.offImage self.itemSize = configuration.itemSize - self.onColor = configuration.onColor - self.offColor = configuration.offColor self.interItemSpacing = configuration.interItemSpacing + self.ratingValueFormat = configuration.ratingValueFormat + self.showsValueLabel = configuration.showsValueLabel + self.averageRating = configuration.averageRating + self.averageRatingFormat = configuration.averageRatingFormat + self.reviewCount = configuration.reviewCount + self.reviewCountFormat = configuration.reviewCountFormat + self.reviewCountCeiling = configuration.reviewCountCeiling + self.reviewCountCeilingFormat = configuration.reviewCountCeilingFormat + self.showsReviewCountLabel = configuration.showsReviewCountLabel self._shouldApplyDefaultStyle = shouldApplyDefaultStyle } } @@ -78,7 +154,7 @@ extension RatingControl: View { if self._shouldApplyDefaultStyle { self.defaultStyle() } else { - self.style.resolve(configuration: .init(rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, onImage: self.onImage, offImage: self.offImage, itemSize: self.itemSize, onColor: self.onColor, offColor: self.offColor, interItemSpacing: self.interItemSpacing)).typeErased + self.style.resolve(configuration: .init(valueLabel: .init(self.valueLabel), onStarImage: .init(self.onStarImage), offStarImage: .init(self.offStarImage), halfStarImage: .init(self.halfStarImage), reviewCountLabel: .init(self.reviewCountLabel), rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, itemSize: self.itemSize, interItemSpacing: self.interItemSpacing, ratingValueFormat: self.ratingValueFormat, showsValueLabel: self.showsValueLabel, averageRating: self.averageRating, averageRatingFormat: self.averageRatingFormat, reviewCount: self.reviewCount, reviewCountFormat: self.reviewCountFormat, reviewCountCeiling: self.reviewCountCeiling, reviewCountCeilingFormat: self.reviewCountCeilingFormat, showsReviewCountLabel: self.showsReviewCountLabel)).typeErased .transformEnvironment(\.ratingControlStyleStack) { stack in if !stack.isEmpty { stack.removeLast() @@ -96,7 +172,7 @@ private extension RatingControl { } func defaultStyle() -> some View { - RatingControl(.init(rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, onImage: self.onImage, offImage: self.offImage, itemSize: self.itemSize, onColor: self.onColor, offColor: self.offColor, interItemSpacing: self.interItemSpacing)) + RatingControl(.init(valueLabel: .init(self.valueLabel), onStarImage: .init(self.onStarImage), offStarImage: .init(self.offStarImage), halfStarImage: .init(self.halfStarImage), reviewCountLabel: .init(self.reviewCountLabel), rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, itemSize: self.itemSize, interItemSpacing: self.interItemSpacing, ratingValueFormat: self.ratingValueFormat, showsValueLabel: self.showsValueLabel, averageRating: self.averageRating, averageRatingFormat: self.averageRatingFormat, reviewCount: self.reviewCount, reviewCountFormat: self.reviewCountFormat, reviewCountCeiling: self.reviewCountCeiling, reviewCountCeilingFormat: self.reviewCountCeilingFormat, showsReviewCountLabel: self.showsReviewCountLabel)) .shouldApplyDefaultStyle(false) .ratingControlStyle(RatingControlFioriStyle.ContentFioriStyle()) .typeErased diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControlStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControlStyle.generated.swift index 7dc60b9b5..fa9996a73 100644 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControlStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControl/RatingControlStyle.generated.swift @@ -22,19 +22,40 @@ struct AnyRatingControlStyle: RatingControlStyle { } public struct RatingControlConfiguration { + public let valueLabel: ValueLabel + public let onStarImage: OnStarImage + public let offStarImage: OffStarImage + public let halfStarImage: HalfStarImage + public let reviewCountLabel: ReviewCountLabel @Binding public var rating: Int public let ratingControlStyle: RatingControl.Style public let ratingBounds: ClosedRange - public let onImage: Image? - public let offImage: Image? public let itemSize: CGSize? - public let onColor: Color? - public let offColor: Color? public let interItemSpacing: CGFloat? + public let ratingValueFormat: String? + public let showsValueLabel: Bool + public let averageRating: CGFloat? + public let averageRatingFormat: String + public let reviewCount: Int? + public let reviewCountFormat: String? + public let reviewCountCeiling: Int? + public let reviewCountCeilingFormat: String? + public let showsReviewCountLabel: Bool + + public typealias ValueLabel = ConfigurationViewWrapper + public typealias OnStarImage = ConfigurationViewWrapper + public typealias OffStarImage = ConfigurationViewWrapper + public typealias HalfStarImage = ConfigurationViewWrapper + public typealias ReviewCountLabel = ConfigurationViewWrapper } public struct RatingControlFioriStyle: RatingControlStyle { public func makeBody(_ configuration: RatingControlConfiguration) -> some View { RatingControl(configuration) + .valueLabelStyle(ValueLabelFioriStyle(ratingControlConfiguration: configuration)) + .onStarImageStyle(OnStarImageFioriStyle(ratingControlConfiguration: configuration)) + .offStarImageStyle(OffStarImageFioriStyle(ratingControlConfiguration: configuration)) + .halfStarImageStyle(HalfStarImageFioriStyle(ratingControlConfiguration: configuration)) + .reviewCountLabelStyle(ReviewCountLabelFioriStyle(ratingControlConfiguration: configuration)) } } diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormView.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormView.generated.swift index 089409e86..adb15321d 100644 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormView.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormView.generated.swift @@ -3,32 +3,49 @@ import Foundation import SwiftUI +import FioriThemeManager + /// The form view which contains a title, rating control, and a subtitle public struct RatingControlFormView { let title: any View + let valueLabel: any View + /// The image to be used for "On" rating star. + let onStarImage: any View + /// / The image to be used for "Off" rating star. + let offStarImage: any View + /// / The image to be used for "half" rating star. + let halfStarImage: any View + let reviewCountLabel: any View /// The rating value. @Binding var rating: Int /// The style of this `RatingControl`. let ratingControlStyle: RatingControl.Style /// The range of the rating values. The default is `0...5`. let ratingBounds: ClosedRange - /// The custom image to be used for "On". - let onImage: Image? - /// The custom image to be used for "Off". - let offImage: Image? /// The custom fixed size of each item image view. let itemSize: CGSize? - /// The custom color for the ON image. - let onColor: Color? - /// The custom color for the OFF image. - let offColor: Color? /// The custom spacing between images. let interItemSpacing: CGFloat? + /// The rating format for displaying rating value. + /// When this is `nil`, the default format is "%d of %d" where "of" is the localized "of". The first parameter is the rating value while the second parameter is the total number of stars. + let ratingValueFormat: String? + /// This property indicates if the value label is to be displayed or not. The default value is `false` for backward compatibility. + let showsValueLabel: Bool + /// The average rating for read-only style. + let averageRating: CGFloat? + /// The format for display the average rating. The default is "%.1f" + let averageRatingFormat: String + /// The number of reviews. + let reviewCount: Int? + /// The format for the review count string. The default is "%d reviews" where "reviews" is the localized "reviews" string. + let reviewCountFormat: String? + /// The ceiling number to be displayed for review count. If the `reviewCount` is larger than this number, this number will be displayed with a "+" sign after the number. + let reviewCountCeiling: Int? + /// The format for the review count string when the count is over the ceiling. The default is "%d+ reviews" where "reviews" is the localized "reviews" string. + let reviewCountCeilingFormat: String? + /// This property indicates if the review count label is to be displayed or not. The default value is `false` for backward compatibility. + let showsReviewCountLabel: Bool let subtitle: any View - /// The `ControlState` of the form view. The default is `normal` - let controlState: ControlState - /// The error message of the form view. - let errorMessage: AttributedString? /// Indicates if the axis for displaying the title and rating control. let axis: Axis @@ -37,54 +54,78 @@ public struct RatingControlFormView { fileprivate var _shouldApplyDefaultStyle = true public init(@ViewBuilder title: () -> any View, + @ViewBuilder valueLabel: () -> any View = { EmptyView() }, + @ViewBuilder onStarImage: () -> any View, + @ViewBuilder offStarImage: () -> any View, + @ViewBuilder halfStarImage: () -> any View, + @ViewBuilder reviewCountLabel: () -> any View = { EmptyView() }, rating: Binding, ratingControlStyle: RatingControl.Style = .editable, ratingBounds: ClosedRange = 0 ... 5, - onImage: Image? = nil, - offImage: Image? = nil, itemSize: CGSize? = nil, - onColor: Color? = nil, - offColor: Color? = nil, interItemSpacing: CGFloat? = nil, + ratingValueFormat: String? = nil, + showsValueLabel: Bool = false, + averageRating: CGFloat? = nil, + averageRatingFormat: String = "%.1f", + reviewCount: Int? = nil, + reviewCountFormat: String? = nil, + reviewCountCeiling: Int? = nil, + reviewCountCeilingFormat: String? = nil, + showsReviewCountLabel: Bool = false, @ViewBuilder subtitle: () -> any View = { EmptyView() }, - controlState: ControlState = .normal, - errorMessage: AttributedString? = nil, axis: Axis = .horizontal) { self.title = Title(title: title) + self.valueLabel = ValueLabel(valueLabel: valueLabel) + self.onStarImage = OnStarImage(onStarImage: onStarImage) + self.offStarImage = OffStarImage(offStarImage: offStarImage) + self.halfStarImage = HalfStarImage(halfStarImage: halfStarImage) + self.reviewCountLabel = ReviewCountLabel(reviewCountLabel: reviewCountLabel) self._rating = rating self.ratingControlStyle = ratingControlStyle self.ratingBounds = ratingBounds - self.onImage = onImage - self.offImage = offImage self.itemSize = itemSize - self.onColor = onColor - self.offColor = offColor self.interItemSpacing = interItemSpacing + self.ratingValueFormat = ratingValueFormat + self.showsValueLabel = showsValueLabel + self.averageRating = averageRating + self.averageRatingFormat = averageRatingFormat + self.reviewCount = reviewCount + self.reviewCountFormat = reviewCountFormat + self.reviewCountCeiling = reviewCountCeiling + self.reviewCountCeilingFormat = reviewCountCeilingFormat + self.showsReviewCountLabel = showsReviewCountLabel self.subtitle = Subtitle(subtitle: subtitle) - self.controlState = controlState - self.errorMessage = errorMessage self.axis = axis } } public extension RatingControlFormView { init(title: AttributedString, + valueLabel: AttributedString? = nil, + onStarImage: Image = FioriIcon.actions.favorite.renderingMode(.template).resizable(), + offStarImage: Image = FioriIcon.actions.unfavorite.renderingMode(.template).resizable(), + halfStarImage: Image = FioriIcon.actions.halfStar.renderingMode(.template).resizable(), + reviewCountLabel: AttributedString? = nil, rating: Binding, ratingControlStyle: RatingControl.Style = .editable, ratingBounds: ClosedRange = 0 ... 5, - onImage: Image? = nil, - offImage: Image? = nil, itemSize: CGSize? = nil, - onColor: Color? = nil, - offColor: Color? = nil, interItemSpacing: CGFloat? = nil, + ratingValueFormat: String? = nil, + showsValueLabel: Bool = false, + averageRating: CGFloat? = nil, + averageRatingFormat: String = "%.1f", + reviewCount: Int? = nil, + reviewCountFormat: String? = nil, + reviewCountCeiling: Int? = nil, + reviewCountCeilingFormat: String? = nil, + showsReviewCountLabel: Bool = false, subtitle: AttributedString? = nil, - controlState: ControlState = .normal, - errorMessage: AttributedString? = nil, axis: Axis = .horizontal) { - self.init(title: { Text(title) }, rating: rating, ratingControlStyle: ratingControlStyle, ratingBounds: ratingBounds, onImage: onImage, offImage: offImage, itemSize: itemSize, onColor: onColor, offColor: offColor, interItemSpacing: interItemSpacing, subtitle: { OptionalText(subtitle) }, controlState: controlState, errorMessage: errorMessage, axis: axis) + self.init(title: { Text(title) }, valueLabel: { OptionalText(valueLabel) }, onStarImage: { onStarImage }, offStarImage: { offStarImage }, halfStarImage: { halfStarImage }, reviewCountLabel: { OptionalText(reviewCountLabel) }, rating: rating, ratingControlStyle: ratingControlStyle, ratingBounds: ratingBounds, itemSize: itemSize, interItemSpacing: interItemSpacing, ratingValueFormat: ratingValueFormat, showsValueLabel: showsValueLabel, averageRating: averageRating, averageRatingFormat: averageRatingFormat, reviewCount: reviewCount, reviewCountFormat: reviewCountFormat, reviewCountCeiling: reviewCountCeiling, reviewCountCeilingFormat: reviewCountCeilingFormat, showsReviewCountLabel: showsReviewCountLabel, subtitle: { OptionalText(subtitle) }, axis: axis) } } @@ -95,18 +136,26 @@ public extension RatingControlFormView { internal init(_ configuration: RatingControlFormViewConfiguration, shouldApplyDefaultStyle: Bool) { self.title = configuration.title + self.valueLabel = configuration.valueLabel + self.onStarImage = configuration.onStarImage + self.offStarImage = configuration.offStarImage + self.halfStarImage = configuration.halfStarImage + self.reviewCountLabel = configuration.reviewCountLabel self._rating = configuration.$rating self.ratingControlStyle = configuration.ratingControlStyle self.ratingBounds = configuration.ratingBounds - self.onImage = configuration.onImage - self.offImage = configuration.offImage self.itemSize = configuration.itemSize - self.onColor = configuration.onColor - self.offColor = configuration.offColor self.interItemSpacing = configuration.interItemSpacing + self.ratingValueFormat = configuration.ratingValueFormat + self.showsValueLabel = configuration.showsValueLabel + self.averageRating = configuration.averageRating + self.averageRatingFormat = configuration.averageRatingFormat + self.reviewCount = configuration.reviewCount + self.reviewCountFormat = configuration.reviewCountFormat + self.reviewCountCeiling = configuration.reviewCountCeiling + self.reviewCountCeilingFormat = configuration.reviewCountCeilingFormat + self.showsReviewCountLabel = configuration.showsReviewCountLabel self.subtitle = configuration.subtitle - self.controlState = configuration.controlState - self.errorMessage = configuration.errorMessage self.axis = configuration.axis self._shouldApplyDefaultStyle = shouldApplyDefaultStyle } @@ -117,7 +166,7 @@ extension RatingControlFormView: View { if self._shouldApplyDefaultStyle { self.defaultStyle() } else { - self.style.resolve(configuration: .init(title: .init(self.title), rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, onImage: self.onImage, offImage: self.offImage, itemSize: self.itemSize, onColor: self.onColor, offColor: self.offColor, interItemSpacing: self.interItemSpacing, subtitle: .init(self.subtitle), controlState: self.controlState, errorMessage: self.errorMessage, axis: self.axis)).typeErased + self.style.resolve(configuration: .init(title: .init(self.title), valueLabel: .init(self.valueLabel), onStarImage: .init(self.onStarImage), offStarImage: .init(self.offStarImage), halfStarImage: .init(self.halfStarImage), reviewCountLabel: .init(self.reviewCountLabel), rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, itemSize: self.itemSize, interItemSpacing: self.interItemSpacing, ratingValueFormat: self.ratingValueFormat, showsValueLabel: self.showsValueLabel, averageRating: self.averageRating, averageRatingFormat: self.averageRatingFormat, reviewCount: self.reviewCount, reviewCountFormat: self.reviewCountFormat, reviewCountCeiling: self.reviewCountCeiling, reviewCountCeilingFormat: self.reviewCountCeilingFormat, showsReviewCountLabel: self.showsReviewCountLabel, subtitle: .init(self.subtitle), axis: self.axis)).typeErased .transformEnvironment(\.ratingControlFormViewStyleStack) { stack in if !stack.isEmpty { stack.removeLast() @@ -135,7 +184,7 @@ private extension RatingControlFormView { } func defaultStyle() -> some View { - RatingControlFormView(.init(title: .init(self.title), rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, onImage: self.onImage, offImage: self.offImage, itemSize: self.itemSize, onColor: self.onColor, offColor: self.offColor, interItemSpacing: self.interItemSpacing, subtitle: .init(self.subtitle), controlState: self.controlState, errorMessage: self.errorMessage, axis: self.axis)) + RatingControlFormView(.init(title: .init(self.title), valueLabel: .init(self.valueLabel), onStarImage: .init(self.onStarImage), offStarImage: .init(self.offStarImage), halfStarImage: .init(self.halfStarImage), reviewCountLabel: .init(self.reviewCountLabel), rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, itemSize: self.itemSize, interItemSpacing: self.interItemSpacing, ratingValueFormat: self.ratingValueFormat, showsValueLabel: self.showsValueLabel, averageRating: self.averageRating, averageRatingFormat: self.averageRatingFormat, reviewCount: self.reviewCount, reviewCountFormat: self.reviewCountFormat, reviewCountCeiling: self.reviewCountCeiling, reviewCountCeilingFormat: self.reviewCountCeilingFormat, showsReviewCountLabel: self.showsReviewCountLabel, subtitle: .init(self.subtitle), axis: self.axis)) .shouldApplyDefaultStyle(false) .ratingControlFormViewStyle(RatingControlFormViewFioriStyle.ContentFioriStyle()) .typeErased diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormViewStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormViewStyle.generated.swift index ff1ec13b4..ce10ad649 100644 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormViewStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/RatingControlFormView/RatingControlFormViewStyle.generated.swift @@ -23,21 +23,34 @@ struct AnyRatingControlFormViewStyle: RatingControlFormViewStyle { public struct RatingControlFormViewConfiguration { public let title: Title + public let valueLabel: ValueLabel + public let onStarImage: OnStarImage + public let offStarImage: OffStarImage + public let halfStarImage: HalfStarImage + public let reviewCountLabel: ReviewCountLabel @Binding public var rating: Int public let ratingControlStyle: RatingControl.Style public let ratingBounds: ClosedRange - public let onImage: Image? - public let offImage: Image? public let itemSize: CGSize? - public let onColor: Color? - public let offColor: Color? public let interItemSpacing: CGFloat? + public let ratingValueFormat: String? + public let showsValueLabel: Bool + public let averageRating: CGFloat? + public let averageRatingFormat: String + public let reviewCount: Int? + public let reviewCountFormat: String? + public let reviewCountCeiling: Int? + public let reviewCountCeilingFormat: String? + public let showsReviewCountLabel: Bool public let subtitle: Subtitle - public let controlState: ControlState - public let errorMessage: AttributedString? public let axis: Axis public typealias Title = ConfigurationViewWrapper + public typealias ValueLabel = ConfigurationViewWrapper + public typealias OnStarImage = ConfigurationViewWrapper + public typealias OffStarImage = ConfigurationViewWrapper + public typealias HalfStarImage = ConfigurationViewWrapper + public typealias ReviewCountLabel = ConfigurationViewWrapper public typealias Subtitle = ConfigurationViewWrapper } @@ -45,8 +58,12 @@ public struct RatingControlFormViewFioriStyle: RatingControlFormViewStyle { public func makeBody(_ configuration: RatingControlFormViewConfiguration) -> some View { RatingControlFormView(configuration) .titleStyle(TitleFioriStyle(ratingControlFormViewConfiguration: configuration)) + .valueLabelStyle(ValueLabelFioriStyle(ratingControlFormViewConfiguration: configuration)) + .onStarImageStyle(OnStarImageFioriStyle(ratingControlFormViewConfiguration: configuration)) + .offStarImageStyle(OffStarImageFioriStyle(ratingControlFormViewConfiguration: configuration)) + .halfStarImageStyle(HalfStarImageFioriStyle(ratingControlFormViewConfiguration: configuration)) + .reviewCountLabelStyle(ReviewCountLabelFioriStyle(ratingControlFormViewConfiguration: configuration)) .subtitleStyle(SubtitleFioriStyle(ratingControlFormViewConfiguration: configuration)) .ratingControlStyle(RatingControlFioriStyle(ratingControlFormViewConfiguration: configuration)) - .formViewStyle(FormViewFioriStyle(ratingControlFormViewConfiguration: configuration)) } } diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/ReviewCountLabel/ReviewCountLabel.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/ReviewCountLabel/ReviewCountLabel.generated.swift new file mode 100644 index 000000000..c5a070c33 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/ReviewCountLabel/ReviewCountLabel.generated.swift @@ -0,0 +1,63 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public struct ReviewCountLabel { + let reviewCountLabel: any View + + @Environment(\.reviewCountLabelStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder reviewCountLabel: () -> any View = { EmptyView() }) { + self.reviewCountLabel = reviewCountLabel() + } +} + +public extension ReviewCountLabel { + init(reviewCountLabel: AttributedString? = nil) { + self.init(reviewCountLabel: { OptionalText(reviewCountLabel) }) + } +} + +public extension ReviewCountLabel { + init(_ configuration: ReviewCountLabelConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: ReviewCountLabelConfiguration, shouldApplyDefaultStyle: Bool) { + self.reviewCountLabel = configuration.reviewCountLabel + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension ReviewCountLabel: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(reviewCountLabel: .init(self.reviewCountLabel))).typeErased + .transformEnvironment(\.reviewCountLabelStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension ReviewCountLabel { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + ReviewCountLabel(.init(reviewCountLabel: .init(self.reviewCountLabel))) + .shouldApplyDefaultStyle(false) + .reviewCountLabelStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/ReviewCountLabel/ReviewCountLabelStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/ReviewCountLabel/ReviewCountLabelStyle.generated.swift new file mode 100644 index 000000000..c1d762c8c --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/ReviewCountLabel/ReviewCountLabelStyle.generated.swift @@ -0,0 +1,28 @@ +// Generated using Sourcery 2.1.7 — https://github.com/krzysztofzablocki/Sourcery +// DO NOT EDIT +import Foundation +import SwiftUI + +public protocol ReviewCountLabelStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: ReviewCountLabelConfiguration) -> Body +} + +struct AnyReviewCountLabelStyle: ReviewCountLabelStyle { + let content: (ReviewCountLabelConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (ReviewCountLabelConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: ReviewCountLabelConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct ReviewCountLabelConfiguration { + public let reviewCountLabel: ReviewCountLabel + + public typealias ReviewCountLabel = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift index 0390dfb2b..2cb487bd7 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift @@ -1767,6 +1767,20 @@ public extension GreetingTextStyle where Self == GreetingTextFioriStyle { } } +// MARK: HalfStarImageStyle + +public extension HalfStarImageStyle where Self == HalfStarImageBaseStyle { + static var base: HalfStarImageBaseStyle { + HalfStarImageBaseStyle() + } +} + +public extension HalfStarImageStyle where Self == HalfStarImageFioriStyle { + static var fiori: HalfStarImageFioriStyle { + HalfStarImageFioriStyle() + } +} + // MARK: HeaderActionStyle public extension HeaderActionStyle where Self == HeaderActionBaseStyle { @@ -2971,6 +2985,34 @@ public extension ObjectItemStyle where Self == ObjectItemActionStyle { } } +// MARK: OffStarImageStyle + +public extension OffStarImageStyle where Self == OffStarImageBaseStyle { + static var base: OffStarImageBaseStyle { + OffStarImageBaseStyle() + } +} + +public extension OffStarImageStyle where Self == OffStarImageFioriStyle { + static var fiori: OffStarImageFioriStyle { + OffStarImageFioriStyle() + } +} + +// MARK: OnStarImageStyle + +public extension OnStarImageStyle where Self == OnStarImageBaseStyle { + static var base: OnStarImageBaseStyle { + OnStarImageBaseStyle() + } +} + +public extension OnStarImageStyle where Self == OnStarImageFioriStyle { + static var fiori: OnStarImageFioriStyle { + OnStarImageFioriStyle() + } +} + // MARK: OptionalTitleStyle public extension OptionalTitleStyle where Self == OptionalTitleBaseStyle { @@ -3251,6 +3293,111 @@ public extension RatingControlStyle where Self == RatingControlFioriStyle { } } +public struct RatingControlValueLabelStyle: RatingControlStyle { + let style: any ValueLabelStyle + + public func makeBody(_ configuration: RatingControlConfiguration) -> some View { + RatingControl(configuration) + .valueLabelStyle(self.style) + .typeErased + } +} + +public extension RatingControlStyle where Self == RatingControlValueLabelStyle { + static func valueLabelStyle(_ style: some ValueLabelStyle) -> RatingControlValueLabelStyle { + RatingControlValueLabelStyle(style: style) + } + + static func valueLabelStyle(@ViewBuilder content: @escaping (ValueLabelConfiguration) -> some View) -> RatingControlValueLabelStyle { + let style = AnyValueLabelStyle(content) + return RatingControlValueLabelStyle(style: style) + } +} + +public struct RatingControlOnStarImageStyle: RatingControlStyle { + let style: any OnStarImageStyle + + public func makeBody(_ configuration: RatingControlConfiguration) -> some View { + RatingControl(configuration) + .onStarImageStyle(self.style) + .typeErased + } +} + +public extension RatingControlStyle where Self == RatingControlOnStarImageStyle { + static func onStarImageStyle(_ style: some OnStarImageStyle) -> RatingControlOnStarImageStyle { + RatingControlOnStarImageStyle(style: style) + } + + static func onStarImageStyle(@ViewBuilder content: @escaping (OnStarImageConfiguration) -> some View) -> RatingControlOnStarImageStyle { + let style = AnyOnStarImageStyle(content) + return RatingControlOnStarImageStyle(style: style) + } +} + +public struct RatingControlOffStarImageStyle: RatingControlStyle { + let style: any OffStarImageStyle + + public func makeBody(_ configuration: RatingControlConfiguration) -> some View { + RatingControl(configuration) + .offStarImageStyle(self.style) + .typeErased + } +} + +public extension RatingControlStyle where Self == RatingControlOffStarImageStyle { + static func offStarImageStyle(_ style: some OffStarImageStyle) -> RatingControlOffStarImageStyle { + RatingControlOffStarImageStyle(style: style) + } + + static func offStarImageStyle(@ViewBuilder content: @escaping (OffStarImageConfiguration) -> some View) -> RatingControlOffStarImageStyle { + let style = AnyOffStarImageStyle(content) + return RatingControlOffStarImageStyle(style: style) + } +} + +public struct RatingControlHalfStarImageStyle: RatingControlStyle { + let style: any HalfStarImageStyle + + public func makeBody(_ configuration: RatingControlConfiguration) -> some View { + RatingControl(configuration) + .halfStarImageStyle(self.style) + .typeErased + } +} + +public extension RatingControlStyle where Self == RatingControlHalfStarImageStyle { + static func halfStarImageStyle(_ style: some HalfStarImageStyle) -> RatingControlHalfStarImageStyle { + RatingControlHalfStarImageStyle(style: style) + } + + static func halfStarImageStyle(@ViewBuilder content: @escaping (HalfStarImageConfiguration) -> some View) -> RatingControlHalfStarImageStyle { + let style = AnyHalfStarImageStyle(content) + return RatingControlHalfStarImageStyle(style: style) + } +} + +public struct RatingControlReviewCountLabelStyle: RatingControlStyle { + let style: any ReviewCountLabelStyle + + public func makeBody(_ configuration: RatingControlConfiguration) -> some View { + RatingControl(configuration) + .reviewCountLabelStyle(self.style) + .typeErased + } +} + +public extension RatingControlStyle where Self == RatingControlReviewCountLabelStyle { + static func reviewCountLabelStyle(_ style: some ReviewCountLabelStyle) -> RatingControlReviewCountLabelStyle { + RatingControlReviewCountLabelStyle(style: style) + } + + static func reviewCountLabelStyle(@ViewBuilder content: @escaping (ReviewCountLabelConfiguration) -> some View) -> RatingControlReviewCountLabelStyle { + let style = AnyReviewCountLabelStyle(content) + return RatingControlReviewCountLabelStyle(style: style) + } +} + // MARK: RatingControlFormViewStyle public extension RatingControlFormViewStyle where Self == RatingControlFormViewBaseStyle { @@ -3286,6 +3433,111 @@ public extension RatingControlFormViewStyle where Self == RatingControlFormViewT } } +public struct RatingControlFormViewValueLabelStyle: RatingControlFormViewStyle { + let style: any ValueLabelStyle + + public func makeBody(_ configuration: RatingControlFormViewConfiguration) -> some View { + RatingControlFormView(configuration) + .valueLabelStyle(self.style) + .typeErased + } +} + +public extension RatingControlFormViewStyle where Self == RatingControlFormViewValueLabelStyle { + static func valueLabelStyle(_ style: some ValueLabelStyle) -> RatingControlFormViewValueLabelStyle { + RatingControlFormViewValueLabelStyle(style: style) + } + + static func valueLabelStyle(@ViewBuilder content: @escaping (ValueLabelConfiguration) -> some View) -> RatingControlFormViewValueLabelStyle { + let style = AnyValueLabelStyle(content) + return RatingControlFormViewValueLabelStyle(style: style) + } +} + +public struct RatingControlFormViewOnStarImageStyle: RatingControlFormViewStyle { + let style: any OnStarImageStyle + + public func makeBody(_ configuration: RatingControlFormViewConfiguration) -> some View { + RatingControlFormView(configuration) + .onStarImageStyle(self.style) + .typeErased + } +} + +public extension RatingControlFormViewStyle where Self == RatingControlFormViewOnStarImageStyle { + static func onStarImageStyle(_ style: some OnStarImageStyle) -> RatingControlFormViewOnStarImageStyle { + RatingControlFormViewOnStarImageStyle(style: style) + } + + static func onStarImageStyle(@ViewBuilder content: @escaping (OnStarImageConfiguration) -> some View) -> RatingControlFormViewOnStarImageStyle { + let style = AnyOnStarImageStyle(content) + return RatingControlFormViewOnStarImageStyle(style: style) + } +} + +public struct RatingControlFormViewOffStarImageStyle: RatingControlFormViewStyle { + let style: any OffStarImageStyle + + public func makeBody(_ configuration: RatingControlFormViewConfiguration) -> some View { + RatingControlFormView(configuration) + .offStarImageStyle(self.style) + .typeErased + } +} + +public extension RatingControlFormViewStyle where Self == RatingControlFormViewOffStarImageStyle { + static func offStarImageStyle(_ style: some OffStarImageStyle) -> RatingControlFormViewOffStarImageStyle { + RatingControlFormViewOffStarImageStyle(style: style) + } + + static func offStarImageStyle(@ViewBuilder content: @escaping (OffStarImageConfiguration) -> some View) -> RatingControlFormViewOffStarImageStyle { + let style = AnyOffStarImageStyle(content) + return RatingControlFormViewOffStarImageStyle(style: style) + } +} + +public struct RatingControlFormViewHalfStarImageStyle: RatingControlFormViewStyle { + let style: any HalfStarImageStyle + + public func makeBody(_ configuration: RatingControlFormViewConfiguration) -> some View { + RatingControlFormView(configuration) + .halfStarImageStyle(self.style) + .typeErased + } +} + +public extension RatingControlFormViewStyle where Self == RatingControlFormViewHalfStarImageStyle { + static func halfStarImageStyle(_ style: some HalfStarImageStyle) -> RatingControlFormViewHalfStarImageStyle { + RatingControlFormViewHalfStarImageStyle(style: style) + } + + static func halfStarImageStyle(@ViewBuilder content: @escaping (HalfStarImageConfiguration) -> some View) -> RatingControlFormViewHalfStarImageStyle { + let style = AnyHalfStarImageStyle(content) + return RatingControlFormViewHalfStarImageStyle(style: style) + } +} + +public struct RatingControlFormViewReviewCountLabelStyle: RatingControlFormViewStyle { + let style: any ReviewCountLabelStyle + + public func makeBody(_ configuration: RatingControlFormViewConfiguration) -> some View { + RatingControlFormView(configuration) + .reviewCountLabelStyle(self.style) + .typeErased + } +} + +public extension RatingControlFormViewStyle where Self == RatingControlFormViewReviewCountLabelStyle { + static func reviewCountLabelStyle(_ style: some ReviewCountLabelStyle) -> RatingControlFormViewReviewCountLabelStyle { + RatingControlFormViewReviewCountLabelStyle(style: style) + } + + static func reviewCountLabelStyle(@ViewBuilder content: @escaping (ReviewCountLabelConfiguration) -> some View) -> RatingControlFormViewReviewCountLabelStyle { + let style = AnyReviewCountLabelStyle(content) + return RatingControlFormViewReviewCountLabelStyle(style: style) + } +} + public struct RatingControlFormViewSubtitleStyle: RatingControlFormViewStyle { let style: any SubtitleStyle @@ -3328,24 +3580,17 @@ public extension RatingControlFormViewStyle where Self == RatingControlFormViewR } } -public struct RatingControlFormViewFormViewStyle: RatingControlFormViewStyle { - let style: any FormViewStyle +// MARK: ReviewCountLabelStyle - public func makeBody(_ configuration: RatingControlFormViewConfiguration) -> some View { - RatingControlFormView(configuration) - .formViewStyle(self.style) - .typeErased +public extension ReviewCountLabelStyle where Self == ReviewCountLabelBaseStyle { + static var base: ReviewCountLabelBaseStyle { + ReviewCountLabelBaseStyle() } } -public extension RatingControlFormViewStyle where Self == RatingControlFormViewFormViewStyle { - static func formViewStyle(_ style: some FormViewStyle) -> RatingControlFormViewFormViewStyle { - RatingControlFormViewFormViewStyle(style: style) - } - - static func formViewStyle(@ViewBuilder content: @escaping (FormViewConfiguration) -> some View) -> RatingControlFormViewFormViewStyle { - let style = AnyFormViewStyle(content) - return RatingControlFormViewFormViewStyle(style: style) +public extension ReviewCountLabelStyle where Self == ReviewCountLabelFioriStyle { + static var fiori: ReviewCountLabelFioriStyle { + ReviewCountLabelFioriStyle() } } diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift index 25c8eca93..7bb8863fc 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift @@ -570,6 +570,27 @@ extension EnvironmentValues { } } +// MARK: HalfStarImageStyle + +struct HalfStarImageStyleStackKey: EnvironmentKey { + static let defaultValue: [any HalfStarImageStyle] = [] +} + +extension EnvironmentValues { + var halfStarImageStyle: any HalfStarImageStyle { + self.halfStarImageStyleStack.last ?? .base + } + + var halfStarImageStyleStack: [any HalfStarImageStyle] { + get { + self[HalfStarImageStyleStackKey.self] + } + set { + self[HalfStarImageStyleStackKey.self] = newValue + } + } +} + // MARK: HeaderActionStyle struct HeaderActionStyleStackKey: EnvironmentKey { @@ -1053,6 +1074,48 @@ extension EnvironmentValues { } } +// MARK: OffStarImageStyle + +struct OffStarImageStyleStackKey: EnvironmentKey { + static let defaultValue: [any OffStarImageStyle] = [] +} + +extension EnvironmentValues { + var offStarImageStyle: any OffStarImageStyle { + self.offStarImageStyleStack.last ?? .base + } + + var offStarImageStyleStack: [any OffStarImageStyle] { + get { + self[OffStarImageStyleStackKey.self] + } + set { + self[OffStarImageStyleStackKey.self] = newValue + } + } +} + +// MARK: OnStarImageStyle + +struct OnStarImageStyleStackKey: EnvironmentKey { + static let defaultValue: [any OnStarImageStyle] = [] +} + +extension EnvironmentValues { + var onStarImageStyle: any OnStarImageStyle { + self.onStarImageStyleStack.last ?? .base + } + + var onStarImageStyleStack: [any OnStarImageStyle] { + get { + self[OnStarImageStyleStackKey.self] + } + set { + self[OnStarImageStyleStackKey.self] = newValue + } + } +} + // MARK: OptionalTitleStyle struct OptionalTitleStyleStackKey: EnvironmentKey { @@ -1242,6 +1305,27 @@ extension EnvironmentValues { } } +// MARK: ReviewCountLabelStyle + +struct ReviewCountLabelStyleStackKey: EnvironmentKey { + static let defaultValue: [any ReviewCountLabelStyle] = [] +} + +extension EnvironmentValues { + var reviewCountLabelStyle: any ReviewCountLabelStyle { + self.reviewCountLabelStyleStack.last ?? .base + } + + var reviewCountLabelStyleStack: [any ReviewCountLabelStyle] { + get { + self[ReviewCountLabelStyleStackKey.self] + } + set { + self[ReviewCountLabelStyleStackKey.self] = newValue + } + } +} + // MARK: Row1Style struct Row1StyleStackKey: EnvironmentKey { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift index fe1c3bd6e..5edc2b190 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift @@ -764,6 +764,34 @@ public extension GreetingTextStyle { } } +// MARK: HalfStarImageStyle + +extension ModifiedStyle: HalfStarImageStyle where Style: HalfStarImageStyle { + public func makeBody(_ configuration: HalfStarImageConfiguration) -> some View { + HalfStarImage(configuration) + .halfStarImageStyle(self.style) + .modifier(self.modifier) + } +} + +public struct HalfStarImageStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.halfStarImageStyle(self.style) + } +} + +public extension HalfStarImageStyle { + func modifier(_ modifier: some ViewModifier) -> some HalfStarImageStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some HalfStarImageStyle) -> some HalfStarImageStyle { + style.modifier(HalfStarImageStyleModifier(style: self)) + } +} + // MARK: HeaderActionStyle extension ModifiedStyle: HeaderActionStyle where Style: HeaderActionStyle { @@ -1408,6 +1436,62 @@ public extension ObjectItemStyle { } } +// MARK: OffStarImageStyle + +extension ModifiedStyle: OffStarImageStyle where Style: OffStarImageStyle { + public func makeBody(_ configuration: OffStarImageConfiguration) -> some View { + OffStarImage(configuration) + .offStarImageStyle(self.style) + .modifier(self.modifier) + } +} + +public struct OffStarImageStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.offStarImageStyle(self.style) + } +} + +public extension OffStarImageStyle { + func modifier(_ modifier: some ViewModifier) -> some OffStarImageStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some OffStarImageStyle) -> some OffStarImageStyle { + style.modifier(OffStarImageStyleModifier(style: self)) + } +} + +// MARK: OnStarImageStyle + +extension ModifiedStyle: OnStarImageStyle where Style: OnStarImageStyle { + public func makeBody(_ configuration: OnStarImageConfiguration) -> some View { + OnStarImage(configuration) + .onStarImageStyle(self.style) + .modifier(self.modifier) + } +} + +public struct OnStarImageStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.onStarImageStyle(self.style) + } +} + +public extension OnStarImageStyle { + func modifier(_ modifier: some ViewModifier) -> some OnStarImageStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some OnStarImageStyle) -> some OnStarImageStyle { + style.modifier(OnStarImageStyleModifier(style: self)) + } +} + // MARK: OptionalTitleStyle extension ModifiedStyle: OptionalTitleStyle where Style: OptionalTitleStyle { @@ -1660,6 +1744,34 @@ public extension RatingControlFormViewStyle { } } +// MARK: ReviewCountLabelStyle + +extension ModifiedStyle: ReviewCountLabelStyle where Style: ReviewCountLabelStyle { + public func makeBody(_ configuration: ReviewCountLabelConfiguration) -> some View { + ReviewCountLabel(configuration) + .reviewCountLabelStyle(self.style) + .modifier(self.modifier) + } +} + +public struct ReviewCountLabelStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.reviewCountLabelStyle(self.style) + } +} + +public extension ReviewCountLabelStyle { + func modifier(_ modifier: some ViewModifier) -> some ReviewCountLabelStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some ReviewCountLabelStyle) -> some ReviewCountLabelStyle { + style.modifier(ReviewCountLabelStyleModifier(style: self)) + } +} + // MARK: Row1Style extension ModifiedStyle: Row1Style where Style: Row1Style { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift index 79cc672e9..1ef7477ee 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift @@ -435,6 +435,22 @@ extension GreetingTextStyle { } } +// MARK: HalfStarImageStyle + +struct ResolvedHalfStarImageStyle: View { + let style: Style + let configuration: HalfStarImageConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension HalfStarImageStyle { + func resolve(configuration: HalfStarImageConfiguration) -> some View { + ResolvedHalfStarImageStyle(style: self, configuration: configuration) + } +} + // MARK: HeaderActionStyle struct ResolvedHeaderActionStyle: View { @@ -803,6 +819,38 @@ extension ObjectItemStyle { } } +// MARK: OffStarImageStyle + +struct ResolvedOffStarImageStyle: View { + let style: Style + let configuration: OffStarImageConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension OffStarImageStyle { + func resolve(configuration: OffStarImageConfiguration) -> some View { + ResolvedOffStarImageStyle(style: self, configuration: configuration) + } +} + +// MARK: OnStarImageStyle + +struct ResolvedOnStarImageStyle: View { + let style: Style + let configuration: OnStarImageConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension OnStarImageStyle { + func resolve(configuration: OnStarImageConfiguration) -> some View { + ResolvedOnStarImageStyle(style: self, configuration: configuration) + } +} + // MARK: OptionalTitleStyle struct ResolvedOptionalTitleStyle: View { @@ -947,6 +995,22 @@ extension RatingControlFormViewStyle { } } +// MARK: ReviewCountLabelStyle + +struct ResolvedReviewCountLabelStyle: View { + let style: Style + let configuration: ReviewCountLabelConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension ReviewCountLabelStyle { + func resolve(configuration: ReviewCountLabelConfiguration) -> some View { + ResolvedReviewCountLabelStyle(style: self, configuration: configuration) + } +} + // MARK: Row1Style struct ResolvedRow1Style: View { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/StyleConfiguration+Extension.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/StyleConfiguration+Extension.generated.swift index 84a5420fe..f9f30c855 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/StyleConfiguration+Extension.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/StyleConfiguration+Extension.generated.swift @@ -89,11 +89,7 @@ extension PlaceholderTextFieldConfiguration { extension RatingControlFormViewConfiguration { var _ratingControl: RatingControl { - RatingControl(.init(rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, onImage: self.onImage, offImage: self.offImage, itemSize: self.itemSize, onColor: self.onColor, offColor: self.offColor, interItemSpacing: self.interItemSpacing), shouldApplyDefaultStyle: true) - } - - var _formView: FormView { - FormView(.init(controlState: self.controlState, errorMessage: self.errorMessage), shouldApplyDefaultStyle: true) + RatingControl(.init(valueLabel: .init(self.valueLabel), onStarImage: .init(self.onStarImage), offStarImage: .init(self.offStarImage), halfStarImage: .init(self.halfStarImage), reviewCountLabel: .init(self.reviewCountLabel), rating: self.$rating, ratingControlStyle: self.ratingControlStyle, ratingBounds: self.ratingBounds, itemSize: self.itemSize, interItemSpacing: self.interItemSpacing, ratingValueFormat: self.ratingValueFormat, showsValueLabel: self.showsValueLabel, averageRating: self.averageRating, averageRatingFormat: self.averageRatingFormat, reviewCount: self.reviewCount, reviewCountFormat: self.reviewCountFormat, reviewCountCeiling: self.reviewCountCeiling, reviewCountCeilingFormat: self.reviewCountCeilingFormat, showsReviewCountLabel: self.showsReviewCountLabel), shouldApplyDefaultStyle: true) } } diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift index d748c32af..113ecfd45 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift @@ -462,6 +462,23 @@ public extension View { } } +// MARK: HalfStarImageStyle + +public extension View { + func halfStarImageStyle(_ style: some HalfStarImageStyle) -> some View { + self.transformEnvironment(\.halfStarImageStyleStack) { stack in + stack.append(style) + } + } + + func halfStarImageStyle(@ViewBuilder content: @escaping (HalfStarImageConfiguration) -> some View) -> some View { + self.transformEnvironment(\.halfStarImageStyleStack) { stack in + let style = AnyHalfStarImageStyle(content) + stack.append(style) + } + } +} + // MARK: HeaderActionStyle public extension View { @@ -853,6 +870,40 @@ public extension View { } } +// MARK: OffStarImageStyle + +public extension View { + func offStarImageStyle(_ style: some OffStarImageStyle) -> some View { + self.transformEnvironment(\.offStarImageStyleStack) { stack in + stack.append(style) + } + } + + func offStarImageStyle(@ViewBuilder content: @escaping (OffStarImageConfiguration) -> some View) -> some View { + self.transformEnvironment(\.offStarImageStyleStack) { stack in + let style = AnyOffStarImageStyle(content) + stack.append(style) + } + } +} + +// MARK: OnStarImageStyle + +public extension View { + func onStarImageStyle(_ style: some OnStarImageStyle) -> some View { + self.transformEnvironment(\.onStarImageStyleStack) { stack in + stack.append(style) + } + } + + func onStarImageStyle(@ViewBuilder content: @escaping (OnStarImageConfiguration) -> some View) -> some View { + self.transformEnvironment(\.onStarImageStyleStack) { stack in + let style = AnyOnStarImageStyle(content) + stack.append(style) + } + } +} + // MARK: OptionalTitleStyle public extension View { @@ -1006,6 +1057,23 @@ public extension View { } } +// MARK: ReviewCountLabelStyle + +public extension View { + func reviewCountLabelStyle(_ style: some ReviewCountLabelStyle) -> some View { + self.transformEnvironment(\.reviewCountLabelStyleStack) { stack in + stack.append(style) + } + } + + func reviewCountLabelStyle(@ViewBuilder content: @escaping (ReviewCountLabelConfiguration) -> some View) -> some View { + self.transformEnvironment(\.reviewCountLabelStyleStack) { stack in + let style = AnyReviewCountLabelStyle(content) + stack.append(style) + } + } +} + // MARK: Row1Style public extension View { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift index 39735d69f..cfe3d7b0a 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift @@ -215,6 +215,12 @@ extension GreetingText: _ViewEmptyChecking { } } +extension HalfStarImage: _ViewEmptyChecking { + public var isEmpty: Bool { + halfStarImage.isEmpty + } +} + extension HeaderAction: _ViewEmptyChecking { public var isEmpty: Bool { headerAction.isEmpty @@ -380,6 +386,18 @@ extension ObjectItem: _ViewEmptyChecking { } } +extension OffStarImage: _ViewEmptyChecking { + public var isEmpty: Bool { + offStarImage.isEmpty + } +} + +extension OnStarImage: _ViewEmptyChecking { + public var isEmpty: Bool { + onStarImage.isEmpty + } +} + extension OptionalTitle: _ViewEmptyChecking { public var isEmpty: Bool { optionalTitle.isEmpty @@ -428,17 +446,32 @@ extension ProfileHeader: _ViewEmptyChecking { extension RatingControl: _ViewEmptyChecking { public var isEmpty: Bool { - false + valueLabel.isEmpty && + onStarImage.isEmpty && + offStarImage.isEmpty && + halfStarImage.isEmpty && + reviewCountLabel.isEmpty } } extension RatingControlFormView: _ViewEmptyChecking { public var isEmpty: Bool { title.isEmpty && + valueLabel.isEmpty && + onStarImage.isEmpty && + offStarImage.isEmpty && + halfStarImage.isEmpty && + reviewCountLabel.isEmpty && subtitle.isEmpty } } +extension ReviewCountLabel: _ViewEmptyChecking { + public var isEmpty: Bool { + reviewCountLabel.isEmpty + } +} + extension Row1: _ViewEmptyChecking { public var isEmpty: Bool { row1.isEmpty diff --git a/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings b/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings index 2fe276f9c..9a882ce20 100644 --- a/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings +++ b/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings @@ -192,3 +192,12 @@ /* XBUT: timeline preview component timestamp label */ "Today, %d" = "Today, %d"; + +/* XFLD: RatingControl value label format */ +"%d of %d" = "%d of %d"; + +/* XFLD: RatingControl review count format */ +"%d reviews" = "%d reviews"; + +/* XFLD: RatingControl review count over ceiling format */ +"%d+ reviews" = "%d+ reviews"; diff --git a/Tests/FioriSwiftUITests/FioriSwiftUICore/RatingControlDataTests.swift b/Tests/FioriSwiftUITests/FioriSwiftUICore/RatingControlDataTests.swift index 3b03f27c4..6b8eb73b7 100644 --- a/Tests/FioriSwiftUITests/FioriSwiftUICore/RatingControlDataTests.swift +++ b/Tests/FioriSwiftUITests/FioriSwiftUICore/RatingControlDataTests.swift @@ -1,4 +1,5 @@ @testable import FioriSwiftUICore +import FioriThemeManager import SwiftUI import XCTest @@ -11,45 +12,44 @@ final class RatingControlDataTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - @State var rating = 2 func testEditable() throws { - let ratingConfig = RatingControlConfiguration(rating: $rating, ratingControlStyle: .editable, ratingBounds: 0 ... 5, onImage: nil, offImage: nil, itemSize: nil, onColor: nil, offColor: nil, interItemSpacing: nil) + let ratingConfig = RatingControlConfiguration(valueLabel: RatingControlConfiguration.ValueLabel(Text("testLabel")), onStarImage: RatingControlConfiguration.OnStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), offStarImage: RatingControlConfiguration.OffStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), halfStarImage: RatingControlConfiguration.HalfStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), reviewCountLabel: RatingControlConfiguration.ReviewCountLabel(Text("reviews")), rating: .constant(2), ratingControlStyle: .editable, ratingBounds: 0 ... 5, itemSize: nil, interItemSpacing: nil, ratingValueFormat: nil, showsValueLabel: false, averageRating: nil, averageRatingFormat: "%.1f", reviewCount: nil, reviewCountFormat: nil, reviewCountCeiling: nil, reviewCountCeilingFormat: nil, showsReviewCountLabel: false) XCTAssertEqual(ratingConfig.getOnColor().cgColor, Color.preferredColor(.tintColor).cgColor) XCTAssertEqual(ratingConfig.getOffColor().cgColor, Color.preferredColor(.tintColor).cgColor) XCTAssertEqual(ratingConfig.getScale(), Image.Scale.large) XCTAssertEqual(ratingConfig.getItemSize(), CGSize(width: 28, height: 28)) - XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(4)) + XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(8)) } func testEditableDisabled() throws { - let ratingConfig = RatingControlConfiguration(rating: $rating, ratingControlStyle: .editableDisabled, ratingBounds: 0 ... 5, onImage: nil, offImage: nil, itemSize: nil, onColor: nil, offColor: nil, interItemSpacing: nil) + let ratingConfig = RatingControlConfiguration(valueLabel: RatingControlConfiguration.ValueLabel(Text("testLabel")), onStarImage: RatingControlConfiguration.OnStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), offStarImage: RatingControlConfiguration.OffStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), halfStarImage: RatingControlConfiguration.HalfStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), reviewCountLabel: RatingControlConfiguration.ReviewCountLabel(Text("reviews")), rating: .constant(2), ratingControlStyle: .editableDisabled, ratingBounds: 0 ... 5, itemSize: nil, interItemSpacing: nil, ratingValueFormat: nil, showsValueLabel: false, averageRating: nil, averageRatingFormat: "%.1f", reviewCount: nil, reviewCountFormat: nil, reviewCountCeiling: nil, reviewCountCeilingFormat: nil, showsReviewCountLabel: false) XCTAssertEqual(ratingConfig.getOnColor().cgColor, Color.preferredColor(.tintColor).cgColor) XCTAssertEqual(ratingConfig.getOffColor().cgColor, Color.preferredColor(.tintColor).cgColor) XCTAssertEqual(ratingConfig.getScale(), Image.Scale.large) XCTAssertEqual(ratingConfig.getItemSize(), CGSize(width: 28, height: 28)) - XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(4)) + XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(8)) } func testStandard() throws { - let ratingConfig = RatingControlConfiguration(rating: $rating, ratingControlStyle: .standard, ratingBounds: 0 ... 5, onImage: nil, offImage: nil, itemSize: nil, onColor: nil, offColor: nil, interItemSpacing: nil) + let ratingConfig = RatingControlConfiguration(valueLabel: RatingControlConfiguration.ValueLabel(Text("testLabel")), onStarImage: RatingControlConfiguration.OnStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), offStarImage: RatingControlConfiguration.OffStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), halfStarImage: RatingControlConfiguration.HalfStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), reviewCountLabel: RatingControlConfiguration.ReviewCountLabel(Text("reviews")), rating: .constant(2), ratingControlStyle: .standard, ratingBounds: 0 ... 5, itemSize: nil, interItemSpacing: nil, ratingValueFormat: nil, showsValueLabel: false, averageRating: nil, averageRatingFormat: "%.1f", reviewCount: nil, reviewCountFormat: nil, reviewCountCeiling: nil, reviewCountCeilingFormat: nil, showsReviewCountLabel: false) XCTAssertEqual(ratingConfig.getOnColor().cgColor, Color.preferredColor(.tertiaryLabel).cgColor) XCTAssertEqual(ratingConfig.getOffColor().cgColor, Color.preferredColor(.tertiaryLabel).cgColor) XCTAssertEqual(ratingConfig.getScale(), Image.Scale.small) XCTAssertEqual(ratingConfig.getItemSize(), CGSize(width: 16, height: 16)) - XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(2)) + XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(6)) } func testAccented() throws { - let ratingConfig = RatingControlConfiguration(rating: $rating, ratingControlStyle: .accented, ratingBounds: 0 ... 5, onImage: nil, offImage: nil, itemSize: nil, onColor: nil, offColor: nil, interItemSpacing: nil) + let ratingConfig = RatingControlConfiguration(valueLabel: RatingControlConfiguration.ValueLabel(Text("testLabel")), onStarImage: RatingControlConfiguration.OnStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), offStarImage: RatingControlConfiguration.OffStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), halfStarImage: RatingControlConfiguration.HalfStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), reviewCountLabel: RatingControlConfiguration.ReviewCountLabel(Text("reviews")), rating: .constant(2), ratingControlStyle: .accented, ratingBounds: 0 ... 5, itemSize: nil, interItemSpacing: nil, ratingValueFormat: nil, showsValueLabel: false, averageRating: nil, averageRatingFormat: "%.1f", reviewCount: nil, reviewCountFormat: nil, reviewCountCeiling: nil, reviewCountCeilingFormat: nil, showsReviewCountLabel: false) XCTAssertEqual(ratingConfig.getOnColor().cgColor, Color.preferredColor(.mango4).cgColor) XCTAssertEqual(ratingConfig.getOffColor().cgColor, Color.preferredColor(.mango4).cgColor) XCTAssertEqual(ratingConfig.getScale(), Image.Scale.small) XCTAssertEqual(ratingConfig.getItemSize(), CGSize(width: 16, height: 16)) - XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(2)) + XCTAssertEqual(ratingConfig.getItemSpacing(), CGFloat(6)) } func testRatingItems() throws { - let ratingConfig = RatingControlConfiguration(rating: $rating, ratingControlStyle: .editable, ratingBounds: 0 ... 5, onImage: nil, offImage: nil, itemSize: nil, onColor: nil, offColor: nil, interItemSpacing: nil) + let ratingConfig = RatingControlConfiguration(valueLabel: RatingControlConfiguration.ValueLabel(Text("testLabel")), onStarImage: RatingControlConfiguration.OnStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), offStarImage: RatingControlConfiguration.OffStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), halfStarImage: RatingControlConfiguration.HalfStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), reviewCountLabel: RatingControlConfiguration.ReviewCountLabel(Text("reviews")), rating: .constant(2), ratingControlStyle: .editable, ratingBounds: 0 ... 5, itemSize: nil, interItemSpacing: nil, ratingValueFormat: nil, showsValueLabel: false, averageRating: nil, averageRatingFormat: "%.1f", reviewCount: nil, reviewCountFormat: nil, reviewCountCeiling: nil, reviewCountCeilingFormat: nil, showsReviewCountLabel: false) let items = ratingConfig.ratingItems(2) XCTAssertEqual(items.count, 5) XCTAssertEqual(items[0].isOn, true) @@ -58,7 +58,7 @@ final class RatingControlDataTests: XCTestCase { XCTAssertEqual(items[3].isOn, false) XCTAssertEqual(items[4].isOn, false) - let ratingConfig2 = RatingControlConfiguration(rating: $rating, ratingControlStyle: .accented, ratingBounds: -5 ... 5, onImage: nil, offImage: nil, itemSize: nil, onColor: nil, offColor: nil, interItemSpacing: nil) + let ratingConfig2 = RatingControlConfiguration(valueLabel: RatingControlConfiguration.ValueLabel(Text("testLabel")), onStarImage: RatingControlConfiguration.OnStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), offStarImage: RatingControlConfiguration.OffStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), halfStarImage: RatingControlConfiguration.HalfStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), reviewCountLabel: RatingControlConfiguration.ReviewCountLabel(Text("reviews")), rating: .constant(2), ratingControlStyle: .accented, ratingBounds: -5 ... 5, itemSize: nil, interItemSpacing: nil, ratingValueFormat: nil, showsValueLabel: false, averageRating: nil, averageRatingFormat: "%.1f", reviewCount: nil, reviewCountFormat: nil, reviewCountCeiling: nil, reviewCountCeilingFormat: nil, showsReviewCountLabel: false) let items2 = ratingConfig2.ratingItems(2) XCTAssertEqual(items2.count, 10) XCTAssertEqual(items2[0].isOn, true) @@ -71,5 +71,14 @@ final class RatingControlDataTests: XCTestCase { XCTAssertEqual(items2[7].isOn, false) XCTAssertEqual(items2[8].isOn, false) XCTAssertEqual(items2[9].isOn, false) + + let ratingConfig3 = RatingControlConfiguration(valueLabel: RatingControlConfiguration.ValueLabel(Text("testLabel")), onStarImage: RatingControlConfiguration.OnStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), offStarImage: RatingControlConfiguration.OffStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), halfStarImage: RatingControlConfiguration.HalfStarImage(FioriIcon.actions.favorite.renderingMode(.template).resizable()), reviewCountLabel: RatingControlConfiguration.ReviewCountLabel(Text("reviews")), rating: .constant(2), ratingControlStyle: .standard, ratingBounds: 0 ... 5, itemSize: nil, interItemSpacing: nil, ratingValueFormat: nil, showsValueLabel: false, averageRating: 3.5, averageRatingFormat: "%.1f", reviewCount: nil, reviewCountFormat: nil, reviewCountCeiling: nil, reviewCountCeilingFormat: nil, showsReviewCountLabel: false) + let items3 = ratingConfig3.ratingItems(3.5) + XCTAssertEqual(items3.count, 5) + XCTAssertEqual(items3[0].isOn, true) + XCTAssertEqual(items3[1].isOn, true) + XCTAssertEqual(items3[2].isOn, true) + XCTAssertEqual(items3[3].isHalf, true) + XCTAssertEqual(items3[4].isOn, false) } }