Skip to content

Commit

Permalink
feat: 🎸 Add secure mode for the TextInputField (#982)
Browse files Browse the repository at this point in the history
Co-authored-by: I824136 <[email protected]>
  • Loading branch information
xiaoqinggrace and xiaoqinggracehe authored Feb 13, 2025
1 parent 72019e0 commit e95c3ab
Show file tree
Hide file tree
Showing 21 changed files with 155 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct TextFieldFormViewExample: View {
@State var hidesReadonlyHint = false
@State var showsAction = false
@State var isRequired = false
@State var isSecureEnabled = false

@State var text = ""

Expand Down Expand Up @@ -64,6 +65,9 @@ struct TextFieldFormViewExample: View {
Toggle("Mandatory Field", isOn: self.$isRequired)
.padding(.leading, 16)
.padding(.trailing, 16)
Toggle("Secure Mode", isOn: self.$isSecureEnabled)
.padding(.leading, 16)
.padding(.trailing, 16)
Button("Dismiss Keyboard") {
hideKeyboard()
}
Expand All @@ -72,21 +76,21 @@ struct TextFieldFormViewExample: View {

Text("Default TextFieldForm")
.italic()
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.getAction1(), actionIconAccessibilityLabel: self.getActionIconAccessibilityLabel())
TextFieldFormView(title: self.key1, text: self.$valueText1, isSecureEnabled: self.isSecureEnabled, 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.getAction1(), actionIconAccessibilityLabel: self.getActionIconAccessibilityLabel())

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, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction2(), actionIconAccessibilityLabel: self.getActionIconAccessibilityLabel())
TextFieldFormView(title: self.key2, text: self.$valueText2, isSecureEnabled: self.isSecureEnabled, 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.getAction2(), actionIconAccessibilityLabel: self.getActionIconAccessibilityLabel())

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, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction3(), actionIconAccessibilityLabel: self.getActionIconAccessibilityLabel())
TextFieldFormView(title: self.key3, text: self.$valueText3, isSecureEnabled: self.isSecureEnabled, 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.getAction3(), actionIconAccessibilityLabel: self.getActionIconAccessibilityLabel())

Text("Disabled")
TextFieldFormView(title: "Disabled Cell", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction4())
TextFieldFormView(title: "Disabled Cell", text: self.$disabledText, isSecureEnabled: self.isSecureEnabled, placeholder: "Disabled", controlState: .disabled, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction4())

Text("Read-Only")
TextFieldFormView(title: "Read-Only Cell", text: self.$readOnlyText, placeholder: "Read-Only", controlState: .readOnly, hidesReadOnlyHint: self.hidesReadonlyHint, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction4())
TextFieldFormView(title: "Read-Only Cell", text: self.$readOnlyText, isSecureEnabled: self.isSecureEnabled, placeholder: "Read-Only", controlState: .readOnly, hidesReadOnlyHint: self.hidesReadonlyHint, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction4())
}
#if !os(visionOS)
.scrollDismissesKeyboard(.immediately)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,6 @@ protocol _MoreActionOverflowComponent {
var moreActionOverflow: (() -> any View)? { get }
}

// sourcery: BaseComponent
protocol _TextInputFieldComponent {
// sourcery: @Binding
var text: String { get }
}

// sourcery: BaseComponent
protocol _GreetingTextComponent {
// sourcery: @ViewBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ protocol _FormViewComponent {
var errorMessage: AttributedString? { get }
}

// sourcery: CompositeComponent
protocol _TextInputFieldComponent {
// sourcery: @Binding
var text: String { get set }
// sourcery: defaultValue = false
var isSecureEnabled: Bool? { get set }
}

// sourcery: CompositeComponent
protocol _PlaceholderTextEditorComponent: _TextViewComponent, _PlaceholderComponent {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public struct PlaceholderTextFieldBaseStyle: PlaceholderTextFieldStyle {
}
}
}
if self.isFocused, !configuration.text.isEmpty {
if self.isFocused, !configuration.text.isEmpty, !(configuration.isSecureEnabled ?? false) {
Button(action: {
configuration.text = ""
}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,15 @@ extension TextFieldFormViewFioriStyle {
}
.frame(minHeight: 44)
.padding(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8))
if self.showsActionButton(configuration), let action = getAction(configuration), let actionIcon = self.getActionIcon(configuration) {
actionIcon
.frame(minHeight: 44)
.padding(.trailing, 8)
.onTapGesture {
action()
}
if !(configuration.isSecureEnabled ?? false) {
if self.showsActionButton(configuration), let action = getAction(configuration), let actionIcon = self.getActionIcon(configuration) {
actionIcon
.frame(minHeight: 44)
.padding(.trailing, 8)
.onTapGesture {
action()
}
}
}
}
.background(RoundedRectangle(cornerRadius: 8).stroke(self.getBorderColor(configuration), lineWidth: self.getBorderWidth(configuration)).background(self.getBackgroundColor(configuration)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,36 @@ import SwiftUI

/// The base layout style for `TextInputField`.
public struct TextInputFieldBaseStyle: TextInputFieldStyle {
@State var isSecure: Bool = false
@ViewBuilder
public func makeBody(_ configuration: TextInputFieldConfiguration) -> some View {
TextField("", text: configuration.$text)
if configuration.isSecureEnabled ?? false {
HStack {
if self.isSecure {
TextField("", text: configuration.$text)
} else {
SecureField("", text: configuration.$text)
}
}
.overlay(alignment: .trailing) {
Image(systemName: self.isSecure ? "eye.fill" : "eye.slash.fill")
.onTapGesture {
self.isSecure.toggle()
}
}
} else {
TextField("", text: configuration.$text)
}
}
}

// Default fiori styles
public struct TextInputFieldFioriStyle: TextInputFieldStyle {
@ViewBuilder
public func makeBody(_ configuration: TextInputFieldConfiguration) -> some View {
TextInputField(configuration)
.frame(minHeight: 44)
/// Default fiori styles
extension TextInputFieldFioriStyle {
struct ContentFioriStyle: TextInputFieldStyle {
func makeBody(_ configuration: TextInputFieldConfiguration) -> some View {
TextInputField(configuration)
.frame(minHeight: 44)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import SwiftUI

public struct PlaceholderTextField {
@Binding var text: String
var isSecureEnabled: Bool?
let placeholder: any View

@Environment(\.placeholderTextFieldStyle) var style
Expand All @@ -14,10 +15,12 @@ public struct PlaceholderTextField {
fileprivate var _shouldApplyDefaultStyle = true

public init(text: Binding<String>,
isSecureEnabled: Bool? = false,
@ViewBuilder placeholder: () -> any View = { EmptyView() },
componentIdentifier: String? = PlaceholderTextField.identifier)
{
self._text = text
self.isSecureEnabled = isSecureEnabled
self.placeholder = Placeholder(placeholder: placeholder, componentIdentifier: componentIdentifier)
self.componentIdentifier = componentIdentifier ?? PlaceholderTextField.identifier
}
Expand All @@ -29,9 +32,10 @@ public extension PlaceholderTextField {

public extension PlaceholderTextField {
init(text: Binding<String>,
isSecureEnabled: Bool? = false,
placeholder: AttributedString? = nil)
{
self.init(text: text, placeholder: { OptionalText(placeholder) })
self.init(text: text, isSecureEnabled: isSecureEnabled, placeholder: { OptionalText(placeholder) })
}
}

Expand All @@ -42,6 +46,7 @@ public extension PlaceholderTextField {

internal init(_ configuration: PlaceholderTextFieldConfiguration, shouldApplyDefaultStyle: Bool) {
self._text = configuration.$text
self.isSecureEnabled = configuration.isSecureEnabled
self.placeholder = configuration.placeholder
self._shouldApplyDefaultStyle = shouldApplyDefaultStyle
self.componentIdentifier = configuration.componentIdentifier
Expand All @@ -53,7 +58,7 @@ extension PlaceholderTextField: View {
if self._shouldApplyDefaultStyle {
self.defaultStyle()
} else {
self.style.resolve(configuration: .init(componentIdentifier: self.componentIdentifier, text: self.$text, placeholder: .init(self.placeholder))).typeErased
self.style.resolve(configuration: .init(componentIdentifier: self.componentIdentifier, text: self.$text, isSecureEnabled: self.isSecureEnabled, placeholder: .init(self.placeholder))).typeErased
.transformEnvironment(\.placeholderTextFieldStyleStack) { stack in
if !stack.isEmpty {
stack.removeLast()
Expand All @@ -71,7 +76,7 @@ private extension PlaceholderTextField {
}

func defaultStyle() -> some View {
PlaceholderTextField(.init(componentIdentifier: self.componentIdentifier, text: self.$text, placeholder: .init(self.placeholder)))
PlaceholderTextField(.init(componentIdentifier: self.componentIdentifier, text: self.$text, isSecureEnabled: self.isSecureEnabled, placeholder: .init(self.placeholder)))
.shouldApplyDefaultStyle(false)
.placeholderTextFieldStyle(PlaceholderTextFieldFioriStyle.ContentFioriStyle())
.typeErased
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct AnyPlaceholderTextFieldStyle: PlaceholderTextFieldStyle {
public struct PlaceholderTextFieldConfiguration {
public var componentIdentifier: String = "fiori_placeholdertextfield_component"
@Binding public var text: String
public let isSecureEnabled: Bool?
public let placeholder: Placeholder

public typealias Placeholder = ConfigurationViewWrapper
Expand All @@ -38,7 +39,7 @@ extension PlaceholderTextFieldConfiguration {
public struct PlaceholderTextFieldFioriStyle: PlaceholderTextFieldStyle {
public func makeBody(_ configuration: PlaceholderTextFieldConfiguration) -> some View {
PlaceholderTextField(configuration)
.textInputFieldStyle(TextInputFieldFioriStyle(placeholderTextFieldConfiguration: configuration))
.placeholderStyle(PlaceholderFioriStyle(placeholderTextFieldConfiguration: configuration))
.textInputFieldStyle(TextInputFieldFioriStyle(placeholderTextFieldConfiguration: configuration))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import FioriThemeManager
public struct StepperField {
let decrementAction: any View
@Binding var text: String
var isSecureEnabled: Bool?
let incrementAction: any View
/// The step value
let step: Double
Expand All @@ -24,6 +25,7 @@ public struct StepperField {

public init(@ViewBuilder decrementAction: () -> any View = { FioriButton { _ in FioriIcon.actions.less } },
text: Binding<String>,
isSecureEnabled: Bool? = false,
@ViewBuilder incrementAction: () -> any View = { FioriButton { _ in FioriIcon.actions.add } },
step: Double = 1,
stepRange: ClosedRange<Double>,
Expand All @@ -32,6 +34,7 @@ public struct StepperField {
{
self.decrementAction = DecrementAction(decrementAction: decrementAction, componentIdentifier: componentIdentifier)
self._text = text
self.isSecureEnabled = isSecureEnabled
self.incrementAction = IncrementAction(incrementAction: incrementAction, componentIdentifier: componentIdentifier)
self.step = step
self.stepRange = stepRange
Expand All @@ -47,12 +50,13 @@ public extension StepperField {
public extension StepperField {
init(decrementAction: FioriButton? = FioriButton { _ in FioriIcon.actions.less },
text: Binding<String>,
isSecureEnabled: Bool? = false,
incrementAction: FioriButton? = FioriButton { _ in FioriIcon.actions.add },
step: Double = 1,
stepRange: ClosedRange<Double>,
isDecimalSupported: Bool = false)
{
self.init(decrementAction: { decrementAction }, text: text, incrementAction: { incrementAction }, step: step, stepRange: stepRange, isDecimalSupported: isDecimalSupported)
self.init(decrementAction: { decrementAction }, text: text, isSecureEnabled: isSecureEnabled, incrementAction: { incrementAction }, step: step, stepRange: stepRange, isDecimalSupported: isDecimalSupported)
}
}

Expand All @@ -64,6 +68,7 @@ public extension StepperField {
internal init(_ configuration: StepperFieldConfiguration, shouldApplyDefaultStyle: Bool) {
self.decrementAction = configuration.decrementAction
self._text = configuration.$text
self.isSecureEnabled = configuration.isSecureEnabled
self.incrementAction = configuration.incrementAction
self.step = configuration.step
self.stepRange = configuration.stepRange
Expand All @@ -78,7 +83,7 @@ extension StepperField: View {
if self._shouldApplyDefaultStyle {
self.defaultStyle()
} else {
self.style.resolve(configuration: .init(componentIdentifier: self.componentIdentifier, decrementAction: .init(self.decrementAction), text: self.$text, incrementAction: .init(self.incrementAction), step: self.step, stepRange: self.stepRange, isDecimalSupported: self.isDecimalSupported)).typeErased
self.style.resolve(configuration: .init(componentIdentifier: self.componentIdentifier, decrementAction: .init(self.decrementAction), text: self.$text, isSecureEnabled: self.isSecureEnabled, incrementAction: .init(self.incrementAction), step: self.step, stepRange: self.stepRange, isDecimalSupported: self.isDecimalSupported)).typeErased
.transformEnvironment(\.stepperFieldStyleStack) { stack in
if !stack.isEmpty {
stack.removeLast()
Expand All @@ -96,7 +101,7 @@ private extension StepperField {
}

func defaultStyle() -> some View {
StepperField(.init(componentIdentifier: self.componentIdentifier, decrementAction: .init(self.decrementAction), text: self.$text, incrementAction: .init(self.incrementAction), step: self.step, stepRange: self.stepRange, isDecimalSupported: self.isDecimalSupported))
StepperField(.init(componentIdentifier: self.componentIdentifier, decrementAction: .init(self.decrementAction), text: self.$text, isSecureEnabled: self.isSecureEnabled, incrementAction: .init(self.incrementAction), step: self.step, stepRange: self.stepRange, isDecimalSupported: self.isDecimalSupported))
.shouldApplyDefaultStyle(false)
.stepperFieldStyle(StepperFieldFioriStyle.ContentFioriStyle())
.typeErased
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public struct StepperFieldConfiguration {
public var componentIdentifier: String = "fiori_stepperfield_component"
public let decrementAction: DecrementAction
@Binding public var text: String
public let isSecureEnabled: Bool?
public let incrementAction: IncrementAction
public let step: Double
public let stepRange: ClosedRange<Double>
Expand All @@ -44,7 +45,7 @@ public struct StepperFieldFioriStyle: StepperFieldStyle {
public func makeBody(_ configuration: StepperFieldConfiguration) -> some View {
StepperField(configuration)
.decrementActionStyle(DecrementActionFioriStyle(stepperFieldConfiguration: configuration))
.textInputFieldStyle(TextInputFieldFioriStyle(stepperFieldConfiguration: configuration))
.incrementActionStyle(IncrementActionFioriStyle(stepperFieldConfiguration: configuration))
.textInputFieldStyle(TextInputFieldFioriStyle(stepperFieldConfiguration: configuration))
}
}
Loading

0 comments on commit e95c3ab

Please sign in to comment.