Skip to content

Commit

Permalink
feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606] Form Cells: Mandator Field (#701)
Browse files Browse the repository at this point in the history
* 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 <xiaoqing.he@sap.com>
  • Loading branch information
xiaoqinggrace and xiaoqinggracehe authored Jun 12, 2024
1 parent a162013 commit 2a2d97f
Show file tree
Hide file tree
Showing 31 changed files with 488 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""

Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions Sources/FioriSwiftUICore/Models/ModelDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 38 additions & 2 deletions Sources/FioriSwiftUICore/Views/SignatureCaptureView+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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"))
}
}
Loading

0 comments on commit 2a2d97f

Please sign in to comment.