From 2a2d97f5f00746b3a7fcaf30ba10321dda300203 Mon Sep 17 00:00:00 2001 From: xiaoqinggrace <52239714+xiaoqinggrace@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:23:31 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20[JIRA-HCPSDKFIORIUIKIT-2?= =?UTF-8?q?606]=20Form=20Cells:=20Mandator=20Field=20(#701)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606] Form Cells: Mandator Field feat: [JIRA-HCPSDKFIORIUIKIT-2606] Form Cells: Mandatory Field Indicator * feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606]Form Cells: Mandatory Field * feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606]Form Cells: Mandatory Field * feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606]Form Cells: Mandatory Field [JIRA-HCPSDKFIORIUIKIT-2606]Form Cells: Mandatory Field Indicator * feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606]Form Cells: Mandatory Field * feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606]Form Cells: Mandatory Field --------- Co-authored-by: I824136 --- .../FormViews/KeyValueFormViewExample.swift | 16 ++-- .../FormViews/TextFieldFormViewExample.swift | 14 ++-- .../SignatureCaptureViewExample.swift | 75 ++++++++++--------- .../Models/ModelDefinitions.swift | 1 + .../Views/SignatureCaptureView+View.swift | 40 +++++++++- .../BaseComponentProtocols.swift | 12 +++ .../CompositeComponentProtocols.swift | 4 +- .../KeyValueFormViewStyle.fiori.swift | 20 ++++- .../MandatoryFieldIndicatorStyle.fiori.swift | 24 ++++++ .../TextFieldFormViewStyle.fiori.swift | 22 +++++- .../KeyValueFormView.generated.swift | 20 +++-- .../KeyValueFormViewStyle.generated.swift | 4 + .../MandatoryFieldIndicator.generated.swift | 63 ++++++++++++++++ ...ndatoryFieldIndicatorStyle.generated.swift | 28 +++++++ .../TextFieldFormView.generated.swift | 16 +++- .../TextFieldFormViewStyle.generated.swift | 4 + ...entStyleProtocol+Extension.generated.swift | 56 ++++++++++++++ .../EnvironmentVariables.generated.swift | 21 ++++++ .../ModifiedStyle.generated.swift | 28 +++++++ .../ResolvedStyle.generated.swift | 16 ++++ .../View+Extension_.generated.swift | 17 +++++ ...iewEmptyChecking+Extension.generated.swift | 12 ++- .../API/ObjectHeader+API.generated.swift | 6 +- .../SearchableListView+API.generated.swift | 2 +- .../SignatureCaptureView+API.generated.swift | 41 +++++----- .../API/SingleStep+API.generated.swift | 10 +-- .../StepProgressIndicator+API.generated.swift | 2 +- .../API/UserConsentForm+API.generated.swift | 2 +- .../API/UserConsentView+API.generated.swift | 2 +- .../en.lproj/FioriSwiftUICore.strings | 3 + sourcery/allPhasesNoCache.sh | 0 31 files changed, 488 insertions(+), 93 deletions(-) create mode 100644 Sources/FioriSwiftUICore/_FioriStyles/MandatoryFieldIndicatorStyle.fiori.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicator.generated.swift create mode 100644 Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicatorStyle.generated.swift mode change 100644 => 100755 sourcery/allPhasesNoCache.sh diff --git a/Apps/Examples/Examples/FioriSwiftUICore/FormViews/KeyValueFormViewExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/FormViews/KeyValueFormViewExample.swift index 057851fdf..1618fcc5e 100755 --- a/Apps/Examples/Examples/FioriSwiftUICore/FormViews/KeyValueFormViewExample.swift +++ b/Apps/Examples/Examples/FioriSwiftUICore/FormViews/KeyValueFormViewExample.swift @@ -41,7 +41,8 @@ struct KeyValueFormViewExample: View { @State var showsCharCount = false @State var allowsBeyondLimit = false @State var hidesReadonlyHint = false - + @State var isRequired = false + var body: some View { VStack { Text("KeyValueFormViewExample") @@ -61,23 +62,26 @@ struct KeyValueFormViewExample: View { Toggle("Hides Read-Only Hint", isOn: self.$hidesReadonlyHint) .padding(.leading, 16) .padding(.trailing, 16) + Toggle("Mandatory Field", isOn: self.$isRequired) + .padding(.leading, 16) + .padding(.trailing, 16) Text("Default KeyValueFormView") - KeyValueFormView(title: self.key1, text: self.$valueText1, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit) + KeyValueFormView(title: self.key1, text: self.$valueText1, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired) Text("Existing Text") .italic() - KeyValueFormView(title: self.key2, text: self.$valueText2, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit) + KeyValueFormView(title: self.key2, text: self.$valueText2, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired) Text("minHeight 50, maxHeight 200") .italic() - KeyValueFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), minTextEditorHeight: 50, maxTextEditorHeight: 200, hintText: self.getHintText(), allowsBeyondLimit: self.allowsBeyondLimit) + KeyValueFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), minTextEditorHeight: 50, maxTextEditorHeight: 200, hintText: self.getHintText(), allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired) Text("Disabled") - KeyValueFormView(title: "Disabled", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, minTextEditorHeight: 50, maxTextEditorHeight: 200) + KeyValueFormView(title: "Disabled", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, minTextEditorHeight: 50, maxTextEditorHeight: 200, isRequired: self.isRequired) Text("Read-Only") - KeyValueFormView(title: "Read-Only", text: self.$readOnlyText, placeholder: "Read-Only", controlState: .readOnly, minTextEditorHeight: 50, maxTextLength: 200, hidesReadOnlyHint: self.hidesReadonlyHint) + KeyValueFormView(title: "Read-Only", text: self.$readOnlyText, placeholder: "Read-Only", controlState: .readOnly, minTextEditorHeight: 50, maxTextLength: 200, hidesReadOnlyHint: self.hidesReadonlyHint, isRequired: self.isRequired) } .scrollDismissesKeyboard(.immediately) } diff --git a/Apps/Examples/Examples/FioriSwiftUICore/FormViews/TextFieldFormViewExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/FormViews/TextFieldFormViewExample.swift index 23682afe3..5598c8939 100644 --- a/Apps/Examples/Examples/FioriSwiftUICore/FormViews/TextFieldFormViewExample.swift +++ b/Apps/Examples/Examples/FioriSwiftUICore/FormViews/TextFieldFormViewExample.swift @@ -35,6 +35,7 @@ struct TextFieldFormViewExample: View { @State var allowsBeyondLimit = false @State var hidesReadonlyHint = false @State var showsAction = false + @State var isRequired = false @State var text = "" @@ -60,23 +61,26 @@ struct TextFieldFormViewExample: View { Toggle("Shows Action", isOn: self.$showsAction) .padding(.leading, 16) .padding(.trailing, 16) + Toggle("Mandatory Field", isOn: self.$isRequired) + .padding(.leading, 16) + .padding(.trailing, 16) Text("Default TitleForm") - TextFieldFormView(title: self.key1, text: self.$valueText1, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, actionIcon: self.getActionIcon(), action: self.getAction()) + TextFieldFormView(title: self.key1, text: self.$valueText1, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction()) Text("Existing Text") .italic() - TextFieldFormView(title: self.key2, text: self.$valueText2, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, actionIcon: self.getActionIcon(), action: self.getAction()) + TextFieldFormView(title: self.key2, text: self.$valueText2, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction()) Text("Empty Text") .italic() - TextFieldFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, actionIcon: self.getActionIcon(), action: self.getAction()) + TextFieldFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction()) Text("Disabled") - TextFieldFormView(title: "Disabled Cell", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, actionIcon: self.getActionIcon(), action: self.getAction()) + TextFieldFormView(title: "Disabled Cell", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction()) Text("Read-Only") - TextFieldFormView(title: "Read-Only Cell", text: self.$readOnlyText, placeholder: "Read-Only", controlState: .readOnly, hidesReadOnlyHint: self.hidesReadonlyHint, actionIcon: self.getActionIcon(), action: self.getAction()) + TextFieldFormView(title: "Read-Only Cell", text: self.$readOnlyText, placeholder: "Read-Only", controlState: .readOnly, hidesReadOnlyHint: self.hidesReadonlyHint, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction()) } .scrollDismissesKeyboard(.immediately) } diff --git a/Apps/Examples/Examples/FioriSwiftUICore/SignatureView/SignatureCaptureViewExample.swift b/Apps/Examples/Examples/FioriSwiftUICore/SignatureView/SignatureCaptureViewExample.swift index 6b64361a7..363331539 100644 --- a/Apps/Examples/Examples/FioriSwiftUICore/SignatureView/SignatureCaptureViewExample.swift +++ b/Apps/Examples/Examples/FioriSwiftUICore/SignatureView/SignatureCaptureViewExample.swift @@ -16,45 +16,52 @@ struct SignatureCaptureViewExample: View { } struct SignatureCaptureViewExample2: View { + @State var isRequired = false let startAction = _Action(actionText: "Sign Here", didSelectAction: nil) let restartAction = _Action(actionText: "Sign Again", didSelectAction: nil) let cancelAction = _Action(actionText: "Cancel2") let tapAction = _Action(model: _TapToSignActionDefault()) var body: some View { - SignatureCaptureView(title: "Long Long Long Long Long Long Long Signature", - startAction: self.startAction, - restartAction: self.restartAction, - cancelAction: _Action(actionText: "Cancel2"), - clearAction: _Action(actionText: "ClearClear"), - saveAction: _Action(actionText: "Save Image"), - signatureImage: UIImage(systemName: "scribble")!, - onSave: { uiImage in - let imgSaver = ImageSaver() - imgSaver.writeToPhotoAlbum(image: uiImage) - }) - .titleFont(.callout) - .titleColor(.red) - .cropsImage(true) - .strokeWidth(10) - .strokeColor(.red) - .drawingViewBackgroundColor(.yellow) - .xmarkColor(.green) - .signatureLineColor(.orange) - .hidesXmark(false) - .hidesSignatureLine(true) - .addsTimestampInImage(true) - .timestampFormatter(self.customFormatter()) - .watermarkText("A bird in the hand is worth two in the bush. Behind every great man there's a great woman.") - .watermarkTextAlignment(.right) - .watermarkTextFont(.preferredFont(forTextStyle: .body)) - .watermarkTextColor(.green) - ._drawingViewMaxHeight(300) - .restartActionModifier { - $0.font(.callout).foregroundColor(.red) - } - .startActionModifier { content in - content.font(nil).foregroundColor(.green) - } + VStack { + Toggle("Mandatory Field", isOn: self.$isRequired) + .padding(.leading, 16) + .padding(.trailing, 16) + SignatureCaptureView(title: "Long Long Long Long Long Long Long Signature", + startAction: self.startAction, + restartAction: self.restartAction, + cancelAction: _Action(actionText: "Cancel2"), + clearAction: _Action(actionText: "ClearClear"), + saveAction: _Action(actionText: "Save Image"), + signatureImage: UIImage(systemName: "scribble")!, + onSave: { uiImage in + let imgSaver = ImageSaver() + imgSaver.writeToPhotoAlbum(image: uiImage) + }) + .titleFont(.callout) + .titleColor(.red) + .cropsImage(true) + .strokeWidth(10) + .strokeColor(.red) + .drawingViewBackgroundColor(.yellow) + .xmarkColor(.green) + .signatureLineColor(.orange) + .hidesXmark(false) + .hidesSignatureLine(true) + .addsTimestampInImage(true) + .isRequired(self.isRequired) + .timestampFormatter(self.customFormatter()) + .watermarkText("A bird in the hand is worth two in the bush. Behind every great man there's a great woman.") + .watermarkTextAlignment(.right) + .watermarkTextFont(.preferredFont(forTextStyle: .body)) + .watermarkTextColor(.green) + ._drawingViewMaxHeight(300) + .restartActionModifier { + $0.font(.callout).foregroundColor(.red) + } + .startActionModifier { content in + content.font(nil).foregroundColor(.green) + } + } } func customFormatter() -> DateFormatter { diff --git a/Sources/FioriSwiftUICore/Models/ModelDefinitions.swift b/Sources/FioriSwiftUICore/Models/ModelDefinitions.swift index 6d7618f20..1c56aa8a1 100644 --- a/Sources/FioriSwiftUICore/Models/ModelDefinitions.swift +++ b/Sources/FioriSwiftUICore/Models/ModelDefinitions.swift @@ -327,6 +327,7 @@ public protocol UserConsentPageModel: TitleComponent, BodyAttributedTextComponen // sourcery: virtualPropWatermarkTextColor = "var watermarkTextColor: Color = .preferredColor(.tertiaryLabel)" // sourcery: virtualPropAppliesTintColorToImage = "var appliesTintColorToImage = true" // sourcery: generated_component_composite +// sourcery: virtualPropIsRequired = "var isRequired = false" public protocol SignatureCaptureViewModel: AnyObject { // sourcery: default.value = nil // sourcery: no_view diff --git a/Sources/FioriSwiftUICore/Views/SignatureCaptureView+View.swift b/Sources/FioriSwiftUICore/Views/SignatureCaptureView+View.swift index 6ea6adc27..da6239cbd 100644 --- a/Sources/FioriSwiftUICore/Views/SignatureCaptureView+View.swift +++ b/Sources/FioriSwiftUICore/Views/SignatureCaptureView+View.swift @@ -76,11 +76,12 @@ extension SignatureCaptureView: View { public var body: some View { VStack(spacing: 0) { HStack { - Text(_title ?? NSLocalizedString("Signature", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "")) + Text(self.getKeyName()) .font(titleFont) .foregroundColor(titleColor) .padding(.top, 11) .padding(.bottom, 11) + .accessibilityLabel(self.getTitleAccessibilityLabel()) Spacer() cancelAction .simultaneousGesture( @@ -248,7 +249,30 @@ extension SignatureCaptureView: View { } } } - + + func getKeyName() -> String { + var titleString = _title + if (titleString?.isEmpty) == nil { + titleString = NSLocalizedString("Signature", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "") + } + if isRequired { + return (titleString ?? "") + "*" + } + return titleString ?? "" + } + + func getTitleAccessibilityLabel() -> String { + var accString = "" + if isRequired { + let requiredText = NSLocalizedString("Required Field", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "Required Field") + let labelString = _title?.suffix(1) == "*" ? String(_title?.dropLast(1) ?? "") : (_title ?? "") + accString = labelString + (labelString.isEmpty != nil ? ", " : "") + requiredText + } else { + accString = _title ?? "" + } + return accString + } + func setEditing() { self.isEditing = true } @@ -508,6 +532,18 @@ public extension SignatureCaptureView { newSelf.appliesTintColorToImage = appliesTintColorToImage return newSelf } + + /** + A view modifier to indicate if the component is a mandatory field. + + - parameter `isRequired`: A boolean variable to indicate if the cell is a mandatory field. + The default value is `false`. + */ + func isRequired(_ isRequired: Bool) -> Self { + var newSelf = self + newSelf.isRequired = isRequired + return newSelf + } } private struct VStackPreferenceKey: PreferenceKey { diff --git a/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift b/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift index 5af7744dd..8adc7254a 100755 --- a/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift +++ b/Sources/FioriSwiftUICore/_ComponentProtocols/BaseComponentProtocols.swift @@ -17,6 +17,18 @@ protocol _SubtitleComponent { var subtitle: AttributedString? { get } } +// sourcery: BaseComponent +protocol _MandatoryFieldIndicatorComponent { + // sourcery: resultBuilder.name = @ViewBuilder, resultBuilder.backingComponent = TextOrIconView + // sourcery: defaultValue = .text("*") + var mandatoryFieldIndicator: TextOrIcon? { get } +} + +protocol _MandatoryField: _MandatoryFieldIndicatorComponent { + // sourcery: defaultValue = false + var isRequired: Bool { get } +} + // sourcery: BaseComponent protocol _TagsComponent { // sourcery: resultBuilder.name = @TagBuilder, resultBuilder.backingComponent = TagStack diff --git a/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift b/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift index 0c4bc4bc9..f3781ebbc 100755 --- a/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift +++ b/Sources/FioriSwiftUICore/_ComponentProtocols/CompositeComponentProtocols.swift @@ -84,7 +84,7 @@ protocol _NoteFormViewComponent: _PlaceholderTextEditorComponent, _FormViewCompo } // sourcery: CompositeComponent -protocol _KeyValueFormViewComponent: _TitleComponent, _NoteFormViewComponent {} +protocol _KeyValueFormViewComponent: _TitleComponent, _NoteFormViewComponent, _MandatoryField {} // sourcery: CompositeComponent protocol _PlaceholderTextFieldComponent: _TextInputFieldComponent, _PlaceholderComponent {} @@ -111,7 +111,7 @@ protocol _TitleFormViewComponent: _PlaceholderTextFieldComponent, _FormViewCompo } // sourcery: CompositeComponent -protocol _TextFieldFormViewComponent: _TitleComponent, _TitleFormViewComponent { +protocol _TextFieldFormViewComponent: _TitleComponent, _TitleFormViewComponent, _MandatoryField { /// The icon for the action button. var actionIcon: Image? { get } /// The action to be performed when the action button is tapped. diff --git a/Sources/FioriSwiftUICore/_FioriStyles/KeyValueFormViewStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/KeyValueFormViewStyle.fiori.swift index 45d9cf366..3a478b068 100755 --- a/Sources/FioriSwiftUICore/_FioriStyles/KeyValueFormViewStyle.fiori.swift +++ b/Sources/FioriSwiftUICore/_FioriStyles/KeyValueFormViewStyle.fiori.swift @@ -6,7 +6,13 @@ import SwiftUI public struct KeyValueFormViewBaseStyle: KeyValueFormViewStyle { public func makeBody(_ configuration: KeyValueFormViewConfiguration) -> some View { VStack(alignment: .leading) { - configuration.title + HStack(spacing: 0) { + configuration.title + if configuration.isRequired { + configuration.mandatoryFieldIndicator + } + Spacer() + } configuration._noteFormView } } @@ -70,4 +76,16 @@ extension KeyValueFormViewFioriStyle { NoteFormView(configuration) } } + + struct MandatoryFieldIndicatorFioriStyle: MandatoryFieldIndicatorStyle { + let keyValueFormViewConfiguration: KeyValueFormViewConfiguration + + func makeBody(_ configuration: MandatoryFieldIndicatorConfiguration) -> some View { + MandatoryFieldIndicator(configuration) + .foregroundStyle(Color.preferredColor(self.keyValueFormViewConfiguration.controlState == .disabled ? .separator : .primaryLabel)) + .font(.fiori(forTextStyle: .subheadline, weight: .semibold)) + .padding(.bottom, -4) + .padding(.top, 11) + } + } } diff --git a/Sources/FioriSwiftUICore/_FioriStyles/MandatoryFieldIndicatorStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/MandatoryFieldIndicatorStyle.fiori.swift new file mode 100644 index 000000000..b92e9733f --- /dev/null +++ b/Sources/FioriSwiftUICore/_FioriStyles/MandatoryFieldIndicatorStyle.fiori.swift @@ -0,0 +1,24 @@ +import FioriThemeManager +import Foundation +import SwiftUI + +// Base Layout style +public struct MandatoryFieldIndicatorBaseStyle: MandatoryFieldIndicatorStyle { + @ViewBuilder + public func makeBody(_ configuration: MandatoryFieldIndicatorConfiguration) -> some View { + // Add default layout here + configuration.mandatoryFieldIndicator + } +} + +// Default fiori styles +public struct MandatoryFieldIndicatorFioriStyle: MandatoryFieldIndicatorStyle { + @ViewBuilder + public func makeBody(_ configuration: MandatoryFieldIndicatorConfiguration) -> some View { + MandatoryFieldIndicator(configuration) + // Add default style here + .foregroundStyle(Color.preferredColor(.primaryLabel)) + .font(.fiori(forTextStyle: .subheadline, weight: .semibold)) + .accessibilityLabel(NSLocalizedString("Required Field", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "Required Field")) + } +} diff --git a/Sources/FioriSwiftUICore/_FioriStyles/TextFieldFormViewStyle.fiori.swift b/Sources/FioriSwiftUICore/_FioriStyles/TextFieldFormViewStyle.fiori.swift index e3dd4e3d7..efbd7f67d 100644 --- a/Sources/FioriSwiftUICore/_FioriStyles/TextFieldFormViewStyle.fiori.swift +++ b/Sources/FioriSwiftUICore/_FioriStyles/TextFieldFormViewStyle.fiori.swift @@ -6,7 +6,13 @@ import SwiftUI public struct TextFieldFormViewBaseStyle: TextFieldFormViewStyle { public func makeBody(_ configuration: TextFieldFormViewConfiguration) -> some View { VStack(alignment: .leading) { - configuration.title + HStack(spacing: 0) { + configuration.title + if configuration.isRequired { + configuration.mandatoryFieldIndicator + } + Spacer() + } configuration._titleFormView } } @@ -24,6 +30,11 @@ extension TextFieldFormViewFioriStyle { .foregroundStyle(self.getTitleColor(configuration)) .font(.fiori(forTextStyle: .subheadline, weight: .semibold)) } + .mandatoryFieldIndicatorStyle { indicatorConf in + MandatoryFieldIndicator(indicatorConf) + .foregroundStyle(self.getTitleColor(configuration)) + .font(.fiori(forTextStyle: .subheadline, weight: .semibold)) + } .placeholderTextFieldStyle { config in HStack { PlaceholderTextField(config) @@ -159,4 +170,13 @@ extension TextFieldFormViewFioriStyle { TitleFormView(configuration) } } + + struct MandatoryFieldIndicatorFioriStyle: MandatoryFieldIndicatorStyle { + let textFieldFormViewConfiguration: TextFieldFormViewConfiguration + + func makeBody(_ configuration: MandatoryFieldIndicatorConfiguration) -> some View { + MandatoryFieldIndicator(configuration) + .foregroundStyle(Color.preferredColor(self.textFieldFormViewConfiguration.controlState == .disabled ? .separator : .primaryLabel)) + } + } } diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormView.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormView.generated.swift index c71d8a89d..f3f732187 100755 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormView.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormView.generated.swift @@ -29,6 +29,8 @@ public struct KeyValueFormView { let charCountReachLimitMessage: String? /// The custom error message when the character count exceeds the limitation. If this property is `nil`, the default localized message will be used. let charCountBeyondLimitMsg: String? + let mandatoryFieldIndicator: any View + let isRequired: Bool @Environment(\.keyValueFormViewStyle) var style @@ -47,7 +49,9 @@ public struct KeyValueFormView { isCharCountEnabled: Bool = false, allowsBeyondLimit: Bool = false, charCountReachLimitMessage: String? = nil, - charCountBeyondLimitMsg: String? = nil) + charCountBeyondLimitMsg: String? = nil, + @ViewBuilder mandatoryFieldIndicator: () -> any View = { EmptyView() }, + isRequired: Bool = false) { self.title = Title { title() } self._text = text @@ -63,6 +67,8 @@ public struct KeyValueFormView { self.allowsBeyondLimit = allowsBeyondLimit self.charCountReachLimitMessage = charCountReachLimitMessage self.charCountBeyondLimitMsg = charCountBeyondLimitMsg + self.mandatoryFieldIndicator = MandatoryFieldIndicator { mandatoryFieldIndicator() } + self.isRequired = isRequired } } @@ -80,9 +86,11 @@ public extension KeyValueFormView { isCharCountEnabled: Bool = false, allowsBeyondLimit: Bool = false, charCountReachLimitMessage: String? = nil, - charCountBeyondLimitMsg: String? = nil) + charCountBeyondLimitMsg: String? = nil, + mandatoryFieldIndicator: TextOrIcon? = .text("*"), + isRequired: Bool = false) { - self.init(title: { Text(title) }, text: text, placeholder: { OptionalText(placeholder) }, controlState: controlState, errorMessage: errorMessage, minTextEditorHeight: minTextEditorHeight, maxTextEditorHeight: maxTextEditorHeight, maxTextLength: maxTextLength, hintText: hintText, hidesReadOnlyHint: hidesReadOnlyHint, isCharCountEnabled: isCharCountEnabled, allowsBeyondLimit: allowsBeyondLimit, charCountReachLimitMessage: charCountReachLimitMessage, charCountBeyondLimitMsg: charCountBeyondLimitMsg) + self.init(title: { Text(title) }, text: text, placeholder: { OptionalText(placeholder) }, controlState: controlState, errorMessage: errorMessage, minTextEditorHeight: minTextEditorHeight, maxTextEditorHeight: maxTextEditorHeight, maxTextLength: maxTextLength, hintText: hintText, hidesReadOnlyHint: hidesReadOnlyHint, isCharCountEnabled: isCharCountEnabled, allowsBeyondLimit: allowsBeyondLimit, charCountReachLimitMessage: charCountReachLimitMessage, charCountBeyondLimitMsg: charCountBeyondLimitMsg, mandatoryFieldIndicator: { TextOrIconView(mandatoryFieldIndicator) }, isRequired: isRequired) } } @@ -106,6 +114,8 @@ public extension KeyValueFormView { self.allowsBeyondLimit = configuration.allowsBeyondLimit self.charCountReachLimitMessage = configuration.charCountReachLimitMessage self.charCountBeyondLimitMsg = configuration.charCountBeyondLimitMsg + self.mandatoryFieldIndicator = configuration.mandatoryFieldIndicator + self.isRequired = configuration.isRequired self._shouldApplyDefaultStyle = shouldApplyDefaultStyle } } @@ -115,7 +125,7 @@ extension KeyValueFormView: View { if self._shouldApplyDefaultStyle { self.defaultStyle() } else { - self.style.resolve(configuration: .init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, minTextEditorHeight: self.minTextEditorHeight, maxTextEditorHeight: self.maxTextEditorHeight, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg)).typeErased + self.style.resolve(configuration: .init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, minTextEditorHeight: self.minTextEditorHeight, maxTextEditorHeight: self.maxTextEditorHeight, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg, mandatoryFieldIndicator: .init(self.mandatoryFieldIndicator), isRequired: self.isRequired)).typeErased .transformEnvironment(\.keyValueFormViewStyleStack) { stack in if !stack.isEmpty { stack.removeLast() @@ -133,7 +143,7 @@ private extension KeyValueFormView { } func defaultStyle() -> some View { - KeyValueFormView(.init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, minTextEditorHeight: self.minTextEditorHeight, maxTextEditorHeight: self.maxTextEditorHeight, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg)) + KeyValueFormView(.init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, minTextEditorHeight: self.minTextEditorHeight, maxTextEditorHeight: self.maxTextEditorHeight, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg, mandatoryFieldIndicator: .init(self.mandatoryFieldIndicator), isRequired: self.isRequired)) .shouldApplyDefaultStyle(false) .keyValueFormViewStyle(KeyValueFormViewFioriStyle.ContentFioriStyle()) .typeErased diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormViewStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormViewStyle.generated.swift index 77f2557ea..6b2f1a8ac 100755 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormViewStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/KeyValueFormView/KeyValueFormViewStyle.generated.swift @@ -36,9 +36,12 @@ public struct KeyValueFormViewConfiguration { public let allowsBeyondLimit: Bool public let charCountReachLimitMessage: String? public let charCountBeyondLimitMsg: String? + public let mandatoryFieldIndicator: MandatoryFieldIndicator + public let isRequired: Bool public typealias Title = ConfigurationViewWrapper public typealias Placeholder = ConfigurationViewWrapper + public typealias MandatoryFieldIndicator = ConfigurationViewWrapper } public struct KeyValueFormViewFioriStyle: KeyValueFormViewStyle { @@ -47,6 +50,7 @@ public struct KeyValueFormViewFioriStyle: KeyValueFormViewStyle { .titleStyle(TitleFioriStyle(keyValueFormViewConfiguration: configuration)) .textViewStyle(TextViewFioriStyle(keyValueFormViewConfiguration: configuration)) .placeholderStyle(PlaceholderFioriStyle(keyValueFormViewConfiguration: configuration)) + .mandatoryFieldIndicatorStyle(MandatoryFieldIndicatorFioriStyle(keyValueFormViewConfiguration: configuration)) .noteFormViewStyle(NoteFormViewFioriStyle(keyValueFormViewConfiguration: configuration)) } } diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicator.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicator.generated.swift new file mode 100644 index 000000000..2615db2e0 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicator.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 MandatoryFieldIndicator { + let mandatoryFieldIndicator: any View + + @Environment(\.mandatoryFieldIndicatorStyle) var style + + fileprivate var _shouldApplyDefaultStyle = true + + public init(@ViewBuilder mandatoryFieldIndicator: () -> any View = { EmptyView() }) { + self.mandatoryFieldIndicator = mandatoryFieldIndicator() + } +} + +public extension MandatoryFieldIndicator { + init(mandatoryFieldIndicator: TextOrIcon? = .text("*")) { + self.init(mandatoryFieldIndicator: { TextOrIconView(mandatoryFieldIndicator) }) + } +} + +public extension MandatoryFieldIndicator { + init(_ configuration: MandatoryFieldIndicatorConfiguration) { + self.init(configuration, shouldApplyDefaultStyle: false) + } + + internal init(_ configuration: MandatoryFieldIndicatorConfiguration, shouldApplyDefaultStyle: Bool) { + self.mandatoryFieldIndicator = configuration.mandatoryFieldIndicator + self._shouldApplyDefaultStyle = shouldApplyDefaultStyle + } +} + +extension MandatoryFieldIndicator: View { + public var body: some View { + if self._shouldApplyDefaultStyle { + self.defaultStyle() + } else { + self.style.resolve(configuration: .init(mandatoryFieldIndicator: .init(self.mandatoryFieldIndicator))).typeErased + .transformEnvironment(\.mandatoryFieldIndicatorStyleStack) { stack in + if !stack.isEmpty { + stack.removeLast() + } + } + } + } +} + +private extension MandatoryFieldIndicator { + func shouldApplyDefaultStyle(_ bool: Bool) -> some View { + var s = self + s._shouldApplyDefaultStyle = bool + return s + } + + func defaultStyle() -> some View { + MandatoryFieldIndicator(mandatoryFieldIndicator: { self.mandatoryFieldIndicator }) + .shouldApplyDefaultStyle(false) + .mandatoryFieldIndicatorStyle(.fiori) + .typeErased + } +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicatorStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicatorStyle.generated.swift new file mode 100644 index 000000000..a51fca8e3 --- /dev/null +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/MandatoryFieldIndicator/MandatoryFieldIndicatorStyle.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 MandatoryFieldIndicatorStyle: DynamicProperty { + associatedtype Body: View + + func makeBody(_ configuration: MandatoryFieldIndicatorConfiguration) -> Body +} + +struct AnyMandatoryFieldIndicatorStyle: MandatoryFieldIndicatorStyle { + let content: (MandatoryFieldIndicatorConfiguration) -> any View + + init(@ViewBuilder _ content: @escaping (MandatoryFieldIndicatorConfiguration) -> any View) { + self.content = content + } + + public func makeBody(_ configuration: MandatoryFieldIndicatorConfiguration) -> some View { + self.content(configuration).typeErased + } +} + +public struct MandatoryFieldIndicatorConfiguration { + public let mandatoryFieldIndicator: MandatoryFieldIndicator + + public typealias MandatoryFieldIndicator = ConfigurationViewWrapper +} diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormView.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormView.generated.swift index f91d63da1..65377a279 100644 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormView.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormView.generated.swift @@ -25,6 +25,8 @@ public struct TextFieldFormView { let charCountReachLimitMessage: String? /// The custom error message when the character count exceeds the limitation. If this property is `nil`, the default localized message will be used. let charCountBeyondLimitMsg: String? + let mandatoryFieldIndicator: any View + let isRequired: Bool /// The icon for the action button. let actionIcon: Image? /// The action to be performed when the action button is tapped. @@ -46,6 +48,8 @@ public struct TextFieldFormView { allowsBeyondLimit: Bool = false, charCountReachLimitMessage: String? = nil, charCountBeyondLimitMsg: String? = nil, + @ViewBuilder mandatoryFieldIndicator: () -> any View = { EmptyView() }, + isRequired: Bool = false, actionIcon: Image? = nil, action: (() -> Void)? = nil) { @@ -61,6 +65,8 @@ public struct TextFieldFormView { self.allowsBeyondLimit = allowsBeyondLimit self.charCountReachLimitMessage = charCountReachLimitMessage self.charCountBeyondLimitMsg = charCountBeyondLimitMsg + self.mandatoryFieldIndicator = MandatoryFieldIndicator { mandatoryFieldIndicator() } + self.isRequired = isRequired self.actionIcon = actionIcon self.action = action } @@ -79,10 +85,12 @@ public extension TextFieldFormView { allowsBeyondLimit: Bool = false, charCountReachLimitMessage: String? = nil, charCountBeyondLimitMsg: String? = nil, + mandatoryFieldIndicator: TextOrIcon? = .text("*"), + isRequired: Bool = false, actionIcon: Image? = nil, action: (() -> Void)? = nil) { - self.init(title: { Text(title) }, text: text, placeholder: { OptionalText(placeholder) }, controlState: controlState, errorMessage: errorMessage, maxTextLength: maxTextLength, hintText: hintText, hidesReadOnlyHint: hidesReadOnlyHint, isCharCountEnabled: isCharCountEnabled, allowsBeyondLimit: allowsBeyondLimit, charCountReachLimitMessage: charCountReachLimitMessage, charCountBeyondLimitMsg: charCountBeyondLimitMsg, actionIcon: actionIcon, action: action) + self.init(title: { Text(title) }, text: text, placeholder: { OptionalText(placeholder) }, controlState: controlState, errorMessage: errorMessage, maxTextLength: maxTextLength, hintText: hintText, hidesReadOnlyHint: hidesReadOnlyHint, isCharCountEnabled: isCharCountEnabled, allowsBeyondLimit: allowsBeyondLimit, charCountReachLimitMessage: charCountReachLimitMessage, charCountBeyondLimitMsg: charCountBeyondLimitMsg, mandatoryFieldIndicator: { TextOrIconView(mandatoryFieldIndicator) }, isRequired: isRequired, actionIcon: actionIcon, action: action) } } @@ -104,6 +112,8 @@ public extension TextFieldFormView { self.allowsBeyondLimit = configuration.allowsBeyondLimit self.charCountReachLimitMessage = configuration.charCountReachLimitMessage self.charCountBeyondLimitMsg = configuration.charCountBeyondLimitMsg + self.mandatoryFieldIndicator = configuration.mandatoryFieldIndicator + self.isRequired = configuration.isRequired self.actionIcon = configuration.actionIcon self.action = configuration.action self._shouldApplyDefaultStyle = shouldApplyDefaultStyle @@ -115,7 +125,7 @@ extension TextFieldFormView: View { if self._shouldApplyDefaultStyle { self.defaultStyle() } else { - self.style.resolve(configuration: .init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg, actionIcon: self.actionIcon, action: self.action)).typeErased + self.style.resolve(configuration: .init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg, mandatoryFieldIndicator: .init(self.mandatoryFieldIndicator), isRequired: self.isRequired, actionIcon: self.actionIcon, action: self.action)).typeErased .transformEnvironment(\.textFieldFormViewStyleStack) { stack in if !stack.isEmpty { stack.removeLast() @@ -133,7 +143,7 @@ private extension TextFieldFormView { } func defaultStyle() -> some View { - TextFieldFormView(.init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg, actionIcon: self.actionIcon, action: self.action)) + TextFieldFormView(.init(title: .init(self.title), text: self.$text, placeholder: .init(self.placeholder), controlState: self.controlState, errorMessage: self.errorMessage, maxTextLength: self.maxTextLength, hintText: self.hintText, hidesReadOnlyHint: self.hidesReadOnlyHint, isCharCountEnabled: self.isCharCountEnabled, allowsBeyondLimit: self.allowsBeyondLimit, charCountReachLimitMessage: self.charCountReachLimitMessage, charCountBeyondLimitMsg: self.charCountBeyondLimitMsg, mandatoryFieldIndicator: .init(self.mandatoryFieldIndicator), isRequired: self.isRequired, actionIcon: self.actionIcon, action: self.action)) .shouldApplyDefaultStyle(false) .textFieldFormViewStyle(TextFieldFormViewFioriStyle.ContentFioriStyle()) .typeErased diff --git a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormViewStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormViewStyle.generated.swift index 86d122d46..c88c9a1d2 100644 --- a/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormViewStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/StyleableComponents/TextFieldFormView/TextFieldFormViewStyle.generated.swift @@ -34,11 +34,14 @@ public struct TextFieldFormViewConfiguration { public let allowsBeyondLimit: Bool public let charCountReachLimitMessage: String? public let charCountBeyondLimitMsg: String? + public let mandatoryFieldIndicator: MandatoryFieldIndicator + public let isRequired: Bool public let actionIcon: Image? public let action: (() -> Void)? public typealias Title = ConfigurationViewWrapper public typealias Placeholder = ConfigurationViewWrapper + public typealias MandatoryFieldIndicator = ConfigurationViewWrapper } public struct TextFieldFormViewFioriStyle: TextFieldFormViewStyle { @@ -47,6 +50,7 @@ public struct TextFieldFormViewFioriStyle: TextFieldFormViewStyle { .titleStyle(TitleFioriStyle(textFieldFormViewConfiguration: configuration)) .textInputFieldStyle(TextInputFieldFioriStyle(textFieldFormViewConfiguration: configuration)) .placeholderStyle(PlaceholderFioriStyle(textFieldFormViewConfiguration: configuration)) + .mandatoryFieldIndicatorStyle(MandatoryFieldIndicatorFioriStyle(textFieldFormViewConfiguration: configuration)) .titleFormViewStyle(TitleFormViewFioriStyle(textFieldFormViewConfiguration: configuration)) } } diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift index 2a0f8f5f7..9b673fae7 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ComponentStyleProtocol+Extension.generated.swift @@ -1837,6 +1837,27 @@ public extension KeyValueFormViewStyle where Self == KeyValueFormViewPlaceholder } } +public struct KeyValueFormViewMandatoryFieldIndicatorStyle: KeyValueFormViewStyle { + let style: any MandatoryFieldIndicatorStyle + + public func makeBody(_ configuration: KeyValueFormViewConfiguration) -> some View { + KeyValueFormView(configuration) + .mandatoryFieldIndicatorStyle(self.style) + .typeErased + } +} + +public extension KeyValueFormViewStyle where Self == KeyValueFormViewMandatoryFieldIndicatorStyle { + static func mandatoryFieldIndicatorStyle(_ style: some MandatoryFieldIndicatorStyle) -> KeyValueFormViewMandatoryFieldIndicatorStyle { + KeyValueFormViewMandatoryFieldIndicatorStyle(style: style) + } + + static func mandatoryFieldIndicatorStyle(@ViewBuilder content: @escaping (MandatoryFieldIndicatorConfiguration) -> some View) -> KeyValueFormViewMandatoryFieldIndicatorStyle { + let style = AnyMandatoryFieldIndicatorStyle(content) + return KeyValueFormViewMandatoryFieldIndicatorStyle(style: style) + } +} + public struct KeyValueFormViewNoteFormViewStyle: KeyValueFormViewStyle { let style: any NoteFormViewStyle @@ -2033,6 +2054,20 @@ public extension LinearProgressIndicatorViewStyle where Self == LinearProgressIn } } +// MARK: MandatoryFieldIndicatorStyle + +public extension MandatoryFieldIndicatorStyle where Self == MandatoryFieldIndicatorBaseStyle { + static var base: MandatoryFieldIndicatorBaseStyle { + MandatoryFieldIndicatorBaseStyle() + } +} + +public extension MandatoryFieldIndicatorStyle where Self == MandatoryFieldIndicatorFioriStyle { + static var fiori: MandatoryFieldIndicatorFioriStyle { + MandatoryFieldIndicatorFioriStyle() + } +} + // MARK: MediaImageStyle public extension MediaImageStyle where Self == MediaImageBaseStyle { @@ -3181,6 +3216,27 @@ public extension TextFieldFormViewStyle where Self == TextFieldFormViewPlacehold } } +public struct TextFieldFormViewMandatoryFieldIndicatorStyle: TextFieldFormViewStyle { + let style: any MandatoryFieldIndicatorStyle + + public func makeBody(_ configuration: TextFieldFormViewConfiguration) -> some View { + TextFieldFormView(configuration) + .mandatoryFieldIndicatorStyle(self.style) + .typeErased + } +} + +public extension TextFieldFormViewStyle where Self == TextFieldFormViewMandatoryFieldIndicatorStyle { + static func mandatoryFieldIndicatorStyle(_ style: some MandatoryFieldIndicatorStyle) -> TextFieldFormViewMandatoryFieldIndicatorStyle { + TextFieldFormViewMandatoryFieldIndicatorStyle(style: style) + } + + static func mandatoryFieldIndicatorStyle(@ViewBuilder content: @escaping (MandatoryFieldIndicatorConfiguration) -> some View) -> TextFieldFormViewMandatoryFieldIndicatorStyle { + let style = AnyMandatoryFieldIndicatorStyle(content) + return TextFieldFormViewMandatoryFieldIndicatorStyle(style: style) + } +} + public struct TextFieldFormViewTitleFormViewStyle: TextFieldFormViewStyle { let style: any TitleFormViewStyle diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift index 6eb50d671..14f0b694a 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/EnvironmentVariables.generated.swift @@ -717,6 +717,27 @@ extension EnvironmentValues { } } +// MARK: MandatoryFieldIndicatorStyle + +struct MandatoryFieldIndicatorStyleStackKey: EnvironmentKey { + static let defaultValue: [any MandatoryFieldIndicatorStyle] = [] +} + +extension EnvironmentValues { + var mandatoryFieldIndicatorStyle: any MandatoryFieldIndicatorStyle { + self.mandatoryFieldIndicatorStyleStack.last ?? .base + } + + var mandatoryFieldIndicatorStyleStack: [any MandatoryFieldIndicatorStyle] { + get { + self[MandatoryFieldIndicatorStyleStackKey.self] + } + set { + self[MandatoryFieldIndicatorStyleStackKey.self] = newValue + } + } +} + // MARK: MediaImageStyle struct MediaImageStyleStackKey: EnvironmentKey { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift index b3b064c18..10a704133 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ModifiedStyle.generated.swift @@ -960,6 +960,34 @@ public extension LinearProgressIndicatorViewStyle { } } +// MARK: MandatoryFieldIndicatorStyle + +extension ModifiedStyle: MandatoryFieldIndicatorStyle where Style: MandatoryFieldIndicatorStyle { + public func makeBody(_ configuration: MandatoryFieldIndicatorConfiguration) -> some View { + MandatoryFieldIndicator(configuration) + .mandatoryFieldIndicatorStyle(self.style) + .modifier(self.modifier) + } +} + +public struct MandatoryFieldIndicatorStyleModifier: ViewModifier { + let style: Style + + public func body(content: Content) -> some View { + content.mandatoryFieldIndicatorStyle(self.style) + } +} + +public extension MandatoryFieldIndicatorStyle { + func modifier(_ modifier: some ViewModifier) -> some MandatoryFieldIndicatorStyle { + ModifiedStyle(style: self, modifier: modifier) + } + + func concat(_ style: some MandatoryFieldIndicatorStyle) -> some MandatoryFieldIndicatorStyle { + style.modifier(MandatoryFieldIndicatorStyleModifier(style: self)) + } +} + // MARK: MediaImageStyle extension ModifiedStyle: MediaImageStyle where Style: MediaImageStyle { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift index bc96ab7aa..7db89bbae 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ResolvedStyle.generated.swift @@ -547,6 +547,22 @@ extension LinearProgressIndicatorViewStyle { } } +// MARK: MandatoryFieldIndicatorStyle + +struct ResolvedMandatoryFieldIndicatorStyle: View { + let style: Style + let configuration: MandatoryFieldIndicatorConfiguration + var body: some View { + self.style.makeBody(self.configuration) + } +} + +extension MandatoryFieldIndicatorStyle { + func resolve(configuration: MandatoryFieldIndicatorConfiguration) -> some View { + ResolvedMandatoryFieldIndicatorStyle(style: self, configuration: configuration) + } +} + // MARK: MediaImageStyle struct ResolvedMediaImageStyle: View { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift index 6ddfcf7c9..19a89afee 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/View+Extension_.generated.swift @@ -581,6 +581,23 @@ public extension View { } } +// MARK: MandatoryFieldIndicatorStyle + +public extension View { + func mandatoryFieldIndicatorStyle(_ style: some MandatoryFieldIndicatorStyle) -> some View { + self.transformEnvironment(\.mandatoryFieldIndicatorStyleStack) { stack in + stack.append(style) + } + } + + func mandatoryFieldIndicatorStyle(@ViewBuilder content: @escaping (MandatoryFieldIndicatorConfiguration) -> some View) -> some View { + self.transformEnvironment(\.mandatoryFieldIndicatorStyleStack) { stack in + let style = AnyMandatoryFieldIndicatorStyle(content) + stack.append(style) + } + } +} + // MARK: MediaImageStyle public extension View { diff --git a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift index 8efd37679..ccc2b3511 100755 --- a/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/SupportingFiles/ViewEmptyChecking+Extension.generated.swift @@ -223,7 +223,8 @@ extension JouleWelcomeScreen: _ViewEmptyChecking { extension KeyValueFormView: _ViewEmptyChecking { public var isEmpty: Bool { title.isEmpty && - placeholder.isEmpty + placeholder.isEmpty && + mandatoryFieldIndicator.isEmpty } } @@ -259,6 +260,12 @@ extension LinearProgressIndicatorView: _ViewEmptyChecking { } } +extension MandatoryFieldIndicator: _ViewEmptyChecking { + public var isEmpty: Bool { + mandatoryFieldIndicator.isEmpty + } +} + extension MediaImage: _ViewEmptyChecking { public var isEmpty: Bool { mediaImage.isEmpty @@ -409,7 +416,8 @@ extension Tags: _ViewEmptyChecking { extension TextFieldFormView: _ViewEmptyChecking { public var isEmpty: Bool { title.isEmpty && - placeholder.isEmpty + placeholder.isEmpty && + mandatoryFieldIndicator.isEmpty } } diff --git a/Sources/FioriSwiftUICore/_generated/ViewModels/API/ObjectHeader+API.generated.swift b/Sources/FioriSwiftUICore/_generated/ViewModels/API/ObjectHeader+API.generated.swift index 4a5a33d4c..5fb376434 100644 --- a/Sources/FioriSwiftUICore/_generated/ViewModels/API/ObjectHeader+API.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/ViewModels/API/ObjectHeader+API.generated.swift @@ -25,12 +25,12 @@ public struct ObjectHeader { let _cancelAction: CancelActionView let _doneAction: DoneActionView - var isTopLevel: Bool = true var dataHandler: (() -> ())? = nil + var isTopLevel: Bool = true var contentView: AnyView? = nil private var isModelInit: Bool = false diff --git a/Sources/FioriSwiftUICore/_generated/ViewModels/API/SignatureCaptureView+API.generated.swift b/Sources/FioriSwiftUICore/_generated/ViewModels/API/SignatureCaptureView+API.generated.swift index 37658b519..ae6e6c209 100644 --- a/Sources/FioriSwiftUICore/_generated/ViewModels/API/SignatureCaptureView+API.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/ViewModels/API/SignatureCaptureView+API.generated.swift @@ -19,32 +19,33 @@ public struct SignatureCaptureView Void)? let _onDelete: (() -> Void)? - @State var fullSignatureImage: UIImage? - @State var isReenterTapped = false - var watermarkText: String? - @State var currentDrawing = Drawing() - var strokeWidth: CGFloat = 3.0 - var hidesXmark = false - public private(set) var _heightDidChangePublisher = CurrentValueSubject(0) - var watermarkTextFont: UIFont = .preferredFont(forTextStyle: .caption1) + var isRequired = false var timestampFormatter: DateFormatter? - var drawingViewBackgroundColor = Color.preferredColor(.primaryBackground) - var hidesSignatureLine = false - let _drawingViewMinHeight: CGFloat = 256 - var titleColor = Color.preferredColor(.primaryLabel) var appliesTintColorToImage = true - @State var drawings = [Drawing]() - var strokeColor = Color.preferredColor(.primaryLabel) - var signatureLineColor = Color.preferredColor(.quaternaryLabel) - var addsTimestampInImage: Bool = false - var _drawingViewMaxHeight: CGFloat? var watermarkTextAlignment: NSTextAlignment = .natural - var cropsImage = false var watermarkTextColor: Color = .preferredColor(.tertiaryLabel) - var titleFont = Font.fiori(forTextStyle: .subheadline).weight(.semibold) - @State var isSaved = false + var watermarkTextFont: UIFont = .preferredFont(forTextStyle: .caption1) + let _drawingViewMinHeight: CGFloat = 256 + var watermarkText: String? var xmarkColor = Color.preferredColor(.quaternaryLabel) + var hidesXmark = false + var signatureLineColor = Color.preferredColor(.quaternaryLabel) + @State var isReenterTapped = false + var hidesSignatureLine = false + var strokeWidth: CGFloat = 3.0 + var titleColor = Color.preferredColor(.primaryLabel) + var strokeColor = Color.preferredColor(.primaryLabel) @State var isEditing = false + @State var isSaved = false + var titleFont = Font.fiori(forTextStyle: .subheadline).weight(.semibold) + @State var currentDrawing = Drawing() + @State var drawings = [Drawing]() + var drawingViewBackgroundColor = Color.preferredColor(.primaryBackground) + @State var fullSignatureImage: UIImage? + var cropsImage = false + var _drawingViewMaxHeight: CGFloat? + public private(set) var _heightDidChangePublisher = CurrentValueSubject(0) + var addsTimestampInImage: Bool = false private var isModelInit: Bool = false private var isTitleNil: Bool = false diff --git a/Sources/FioriSwiftUICore/_generated/ViewModels/API/SingleStep+API.generated.swift b/Sources/FioriSwiftUICore/_generated/ViewModels/API/SingleStep+API.generated.swift index ab7a38709..c8a7297fb 100644 --- a/Sources/FioriSwiftUICore/_generated/ViewModels/API/SingleStep+API.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/ViewModels/API/SingleStep+API.generated.swift @@ -14,15 +14,15 @@ public struct SingleStep Void)? let _didDeny: ((Bool) -> Void)? let _didCancel: (() -> Void)? - @State var _pageIndex = 0 @State var _showAlert: (Bool, UserConsentAlertType) = (false, .deny) + @State var _pageIndex = 0 private var isModelInit: Bool = false private var isNextActionNil: Bool = false diff --git a/Sources/FioriSwiftUICore/_generated/ViewModels/API/UserConsentView+API.generated.swift b/Sources/FioriSwiftUICore/_generated/ViewModels/API/UserConsentView+API.generated.swift index 54eff7dbf..9ec153cdf 100644 --- a/Sources/FioriSwiftUICore/_generated/ViewModels/API/UserConsentView+API.generated.swift +++ b/Sources/FioriSwiftUICore/_generated/ViewModels/API/UserConsentView+API.generated.swift @@ -10,8 +10,8 @@ public struct UserConsentView { let _didDeny: ((Int, Bool) -> Void)? let _didCancel: ((Int) -> Void)? let _didFinish: (([Int]) -> Void)? - @State var _allowedFormIndexes: [Int] = [] @State var _formIndex = 0 + @State var _allowedFormIndexes: [Int] = [] private var isModelInit: Bool = false private var isDidAllowNil: Bool = false diff --git a/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings b/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings index 2bc62aef3..5c1b2cfdb 100644 --- a/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings +++ b/Sources/FioriSwiftUICore/_localization/en.lproj/FioriSwiftUICore.strings @@ -31,6 +31,9 @@ /* XACT: The accessibility label for the signature line */ "Signature Line" = "Signature Line"; +/* XACT: The accessibility label for the mandatory field */ +"Required Field" = "Required Field"; + /* XBUT: Action available in inactive state of inline signature form cell, see https://experience.sap.com/fiori-design-ios/article/in-line-signature-form-cell/#behavior-and-interaction */ "Canceled" = "Canceled"; diff --git a/sourcery/allPhasesNoCache.sh b/sourcery/allPhasesNoCache.sh old mode 100644 new mode 100755