Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606] Form Cells: Mandator Field #701

Merged
merged 7 commits into from
Jun 12, 2024
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
}
xiaoqinggrace marked this conversation as resolved.
Show resolved Hide resolved
}

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
Loading