diff --git a/app/CHANGELOG.md b/app/CHANGELOG.md index 3332448c9..56cc48f8d 100644 --- a/app/CHANGELOG.md +++ b/app/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Library] `Filter chip` Apply high contrast theme to filter chip (selected) ([#494](https://github.com/Orange-OpenSource/ouds-flutter/issues/494)) ### Changed +- [DemoApp][Library] `Pin code input` Add optional `keyboardType` parameter (numeric/alphanumeric) ([#733](https://github.com/Orange-OpenSource/ouds-flutter/issues/733)) - [Library] update tokens 1.9.0 - Component Bullet List ([#710](https://github.com/Orange-OpenSource/ouds-flutter/issues/710)) - [Library] update tokens 1.9.0 - Component Alert ([#672](https://github.com/Orange-OpenSource/ouds-flutter/issues/672)) diff --git a/app/lib/l10n/gen/ouds_flutter_app_localizations.dart b/app/lib/l10n/gen/ouds_flutter_app_localizations.dart index 78ad86e93..5fdfa3847 100644 --- a/app/lib/l10n/gen/ouds_flutter_app_localizations.dart +++ b/app/lib/l10n/gen/ouds_flutter_app_localizations.dart @@ -1272,6 +1272,24 @@ abstract class AppLocalizations { /// **'Hidden Password'** String get app_components_pinCodeInput_hidden_password_label; + /// No description provided for @app_components_pinCodeInput_keyboardType_label. + /// + /// In en, this message translates to: + /// **'Keyboard type'** + String get app_components_pinCodeInput_keyboardType_label; + + /// No description provided for @app_components_pinCodeInput_keyboardType_numeric_label. + /// + /// In en, this message translates to: + /// **'Numeric'** + String get app_components_pinCodeInput_keyboardType_numeric_label; + + /// No description provided for @app_components_pinCodeInput_keyboardType_alphanumeric_label. + /// + /// In en, this message translates to: + /// **'Alphanumeric'** + String get app_components_pinCodeInput_keyboardType_alphanumeric_label; + /// No description provided for @app_components_navigationBar_label. /// /// In en, this message translates to: diff --git a/app/lib/l10n/gen/ouds_flutter_app_localizations_ar.dart b/app/lib/l10n/gen/ouds_flutter_app_localizations_ar.dart index f25c4d941..a903745ff 100644 --- a/app/lib/l10n/gen/ouds_flutter_app_localizations_ar.dart +++ b/app/lib/l10n/gen/ouds_flutter_app_localizations_ar.dart @@ -664,6 +664,17 @@ class AppLocalizationsAr extends AppLocalizations { String get app_components_pinCodeInput_hidden_password_label => 'Hidden Password'; + @override + String get app_components_pinCodeInput_keyboardType_label => 'Keyboard type'; + + @override + String get app_components_pinCodeInput_keyboardType_numeric_label => + 'Numeric'; + + @override + String get app_components_pinCodeInput_keyboardType_alphanumeric_label => + 'Alphanumeric'; + @override String get app_components_navigationBar_label => 'Bottom Bar'; diff --git a/app/lib/l10n/gen/ouds_flutter_app_localizations_en.dart b/app/lib/l10n/gen/ouds_flutter_app_localizations_en.dart index 2a2e63057..42e5ed21f 100644 --- a/app/lib/l10n/gen/ouds_flutter_app_localizations_en.dart +++ b/app/lib/l10n/gen/ouds_flutter_app_localizations_en.dart @@ -664,6 +664,17 @@ class AppLocalizationsEn extends AppLocalizations { String get app_components_pinCodeInput_hidden_password_label => 'Hidden Password'; + @override + String get app_components_pinCodeInput_keyboardType_label => 'Keyboard type'; + + @override + String get app_components_pinCodeInput_keyboardType_numeric_label => + 'Numeric'; + + @override + String get app_components_pinCodeInput_keyboardType_alphanumeric_label => + 'Alphanumeric'; + @override String get app_components_navigationBar_label => 'Bottom Bar'; diff --git a/app/lib/l10n/gen/ouds_flutter_app_localizations_fr.dart b/app/lib/l10n/gen/ouds_flutter_app_localizations_fr.dart index ef77c0e4b..d58b3bfcd 100644 --- a/app/lib/l10n/gen/ouds_flutter_app_localizations_fr.dart +++ b/app/lib/l10n/gen/ouds_flutter_app_localizations_fr.dart @@ -668,6 +668,17 @@ class AppLocalizationsFr extends AppLocalizations { String get app_components_pinCodeInput_hidden_password_label => 'Hidden Password'; + @override + String get app_components_pinCodeInput_keyboardType_label => 'Keyboard type'; + + @override + String get app_components_pinCodeInput_keyboardType_numeric_label => + 'Numeric'; + + @override + String get app_components_pinCodeInput_keyboardType_alphanumeric_label => + 'Alphanumeric'; + @override String get app_components_navigationBar_label => 'Bottom Bar'; diff --git a/app/lib/l10n/ouds_flutter_en.arb b/app/lib/l10n/ouds_flutter_en.arb index 4b91f0fda..7f8a66996 100644 --- a/app/lib/l10n/ouds_flutter_en.arb +++ b/app/lib/l10n/ouds_flutter_en.arb @@ -285,6 +285,9 @@ "app_components_pinCodeInput_error_label": "Please enter the verification code.", "app_components_pinCodeInput_verification_error_label": "Verification failed. Check and enter the correct code.", "app_components_pinCodeInput_hidden_password_label": "Hidden Password", + "app_components_pinCodeInput_keyboardType_label": "Keyboard type", + "app_components_pinCodeInput_keyboardType_numeric_label": "Numeric", + "app_components_pinCodeInput_keyboardType_alphanumeric_label": "Alphanumeric", "@_components_navigation_bar": {}, "app_components_navigationBar_label": "Bottom Bar", diff --git a/app/lib/ui/components/pin_code_input/pin_code_input_code_generator.dart b/app/lib/ui/components/pin_code_input/pin_code_input_code_generator.dart index f277afe04..63dc64fa5 100644 --- a/app/lib/ui/components/pin_code_input/pin_code_input_code_generator.dart +++ b/app/lib/ui/components/pin_code_input/pin_code_input_code_generator.dart @@ -13,6 +13,7 @@ import 'package:flutter/material.dart'; import 'package:ouds_flutter_demo/ui/components/pin_code_input/pin_code_input_customization.dart'; import 'package:ouds_flutter_demo/ui/components/pin_code_input/pin_code_input_customization_utils.dart'; +import 'package:ouds_flutter_demo/ui/components/pin_code_input/pin_code_input_enum.dart'; class PinCodeInputCodeGenerator { static String updateCode(BuildContext context) { @@ -60,6 +61,10 @@ class PinCodeInputCodeGenerator { props.add(' isOutlined: ${state.hasOutlined},'); } + if (state.selectedKeyboardType != PinCodeKeyboardTypeEnum.numeric) { + props.add(' keyboardType: OudsPinCodeInputKeyboardType.${state.selectedKeyboardType.name},'); + } + if (props.isEmpty) { return "digitInputDecoration: OudsDigitInputDecoration(),"; } diff --git a/app/lib/ui/components/pin_code_input/pin_code_input_customization.dart b/app/lib/ui/components/pin_code_input/pin_code_input_customization.dart index 084ef181c..acd62d2f0 100644 --- a/app/lib/ui/components/pin_code_input/pin_code_input_customization.dart +++ b/app/lib/ui/components/pin_code_input/pin_code_input_customization.dart @@ -42,6 +42,7 @@ class PinCodeInputCustomizationState extends CustomizationWidgetState outlinedState.value; set hasOutlined(bool value) => outlinedState.value = value; + // Proxy getters and setters to expose the keyboard type selection. + PinCodeKeyboardTypeEnum get selectedKeyboardType => pinCodeKeyboardTypeState.selected; + set selectedKeyboardType(PinCodeKeyboardTypeEnum value) => pinCodeKeyboardTypeState.selected = value; + @override Widget build(BuildContext context) { return _PinCodeInputCustomization( @@ -261,3 +266,25 @@ class OutlinedState { }); } } + +/// Keyboard Type State Management +class PinCodeKeyboardTypeState { + PinCodeKeyboardTypeState(this._setState); + + final void Function(void Function()) _setState; + + final List _list = [ + PinCodeKeyboardTypeEnum.numeric, + PinCodeKeyboardTypeEnum.alphanumeric, + ]; + + List get list => _list; + + PinCodeKeyboardTypeEnum _selected = PinCodeKeyboardTypeEnum.numeric; + PinCodeKeyboardTypeEnum get selected => _selected; + set selected(PinCodeKeyboardTypeEnum newValue) { + _setState(() { + _selected = newValue; + }); + } +} diff --git a/app/lib/ui/components/pin_code_input/pin_code_input_customization_utils.dart b/app/lib/ui/components/pin_code_input/pin_code_input_customization_utils.dart index 42e3072ff..cc5f3f5e2 100644 --- a/app/lib/ui/components/pin_code_input/pin_code_input_customization_utils.dart +++ b/app/lib/ui/components/pin_code_input/pin_code_input_customization_utils.dart @@ -50,4 +50,14 @@ class PinCodeInputCustomizationUtils { final label = customizationState.pinCodeErrorText; return label.isEmpty ? null : label; } + + /// Maps the demo keyboard-type enum to the public [OudsPinCodeInputKeyboardType]. + static OudsPinCodeInputKeyboardType getKeyboardType(PinCodeKeyboardTypeEnum value) { + switch (value) { + case PinCodeKeyboardTypeEnum.numeric: + return OudsPinCodeInputKeyboardType.numeric; + case PinCodeKeyboardTypeEnum.alphanumeric: + return OudsPinCodeInputKeyboardType.alphanumeric; + } + } } \ No newline at end of file diff --git a/app/lib/ui/components/pin_code_input/pin_code_input_demo_screen.dart b/app/lib/ui/components/pin_code_input/pin_code_input_demo_screen.dart index 282fc251b..ff7af4398 100644 --- a/app/lib/ui/components/pin_code_input/pin_code_input_demo_screen.dart +++ b/app/lib/ui/components/pin_code_input/pin_code_input_demo_screen.dart @@ -149,6 +149,7 @@ class _PinCodeInputDemoState extends State<_PinCodeInputDemo> { hiddenPassword: customizationState.hasHiddenPassword, isOutlined: customizationState.hasOutlined, constrainedMaxWidth: customizationState.hasConstrainedMaxWidth ? true : false, + keyboardType: PinCodeInputCustomizationUtils.getKeyboardType(customizationState.selectedKeyboardType), ), onEditingComplete: (value) async { final errorLabel = context.l10n.app_components_pinCodeInput_error_label; @@ -280,6 +281,17 @@ class _CustomizationContentState extends State<_CustomizationContent> { customizationState.hasOutlined = value; }, ), + CustomizableChips( + title: PinCodeKeyboardTypeEnum.enumName(context), + options: customizationState.pinCodeKeyboardTypeState.list, + selectedOption: customizationState.selectedKeyboardType, + getText: (option) => option.stringValue(context), + onSelected: (selectedOption) { + setState(() { + customizationState.selectedKeyboardType = selectedOption; + }); + }, + ), CustomizableTextField( title: context.l10n.app_components_common_placeholder_label, text: customizationState.pinCodePlaceholderText, diff --git a/app/lib/ui/components/pin_code_input/pin_code_input_enum.dart b/app/lib/ui/components/pin_code_input/pin_code_input_enum.dart index 61617601c..d55fe8919 100644 --- a/app/lib/ui/components/pin_code_input/pin_code_input_enum.dart +++ b/app/lib/ui/components/pin_code_input/pin_code_input_enum.dart @@ -42,3 +42,26 @@ extension CustomElementLayout on PinCodeLengthEnum { } } +/// Mirrors [OudsPinCodeInputKeyboardType] for the demo customization layer. +enum PinCodeKeyboardTypeEnum { + numeric, + alphanumeric; + + const PinCodeKeyboardTypeEnum(); + + static String enumName(BuildContext context) { + return context.l10n.app_components_pinCodeInput_keyboardType_label; + } +} + +extension PinCodeKeyboardTypeLabel on PinCodeKeyboardTypeEnum { + String stringValue(BuildContext context) { + switch (this) { + case PinCodeKeyboardTypeEnum.numeric: + return context.l10n.app_components_pinCodeInput_keyboardType_numeric_label; + case PinCodeKeyboardTypeEnum.alphanumeric: + return context.l10n.app_components_pinCodeInput_keyboardType_alphanumeric_label; + } + } +} + diff --git a/ouds_core/CHANGELOG.md b/ouds_core/CHANGELOG.md index 88121d709..7b3afb4a5 100644 --- a/ouds_core/CHANGELOG.md +++ b/ouds_core/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Library] `Filter chip` Apply high contrast theme to filter chip (selected) ([#494](https://github.com/Orange-OpenSource/ouds-flutter/issues/494)) ### Changed +- [Library] `Pin code input` Add optional `keyboardType` parameter (numeric/alphanumeric) ([#733](https://github.com/Orange-OpenSource/ouds-flutter/issues/733)) - [Library] update tokens 1.9.0 - Component Bullet List ([#710](https://github.com/Orange-OpenSource/ouds-flutter/issues/710)) - [Library] update tokens 1.9.0 - Component Alert ([#672](https://github.com/Orange-OpenSource/ouds-flutter/issues/672)) diff --git a/ouds_core/lib/components/pin_code_input/digit_input/ouds_digit_input.dart b/ouds_core/lib/components/pin_code_input/digit_input/ouds_digit_input.dart index 37dbccff8..51048cf8c 100644 --- a/ouds_core/lib/components/pin_code_input/digit_input/ouds_digit_input.dart +++ b/ouds_core/lib/components/pin_code_input/digit_input/ouds_digit_input.dart @@ -40,18 +40,22 @@ import 'package:ouds_theme_contract/ouds_theme.dart'; /// - [constrainedMaxWidth]: When `true`, the item width is constrained to a maximum value defined by the design system. /// When `false`, no specific width constraint is applied, allowing the component to size itself or follow external modifiers. /// Defaults to `false`. +/// - [keyboardType]: Soft keyboard requested when a digit cell is focused. Defaults to [OudsPinCodeInputKeyboardType.numeric]. +/// Use [OudsPinCodeInputKeyboardType.alphanumeric] to allow letters in addition to digits. /// class OudsDigitInputDecoration { final String? hintText; //placeholder final bool hiddenPassword; final bool isOutlined; final bool constrainedMaxWidth; + final OudsPinCodeInputKeyboardType keyboardType; const OudsDigitInputDecoration({ this.hintText, this.hiddenPassword = true, this.isOutlined = false, this.constrainedMaxWidth = false, + this.keyboardType = OudsPinCodeInputKeyboardType.numeric, }); } @@ -196,7 +200,12 @@ class _OudsDigitInputState extends State { cursorColor: pinCodeInputTextModifier.getPinCodeCursorColor(widget.isError), controller: widget.controller, focusNode: widget.focusNode, - keyboardType: TextInputType.number, + keyboardType: widget.digitInputDecoration!.keyboardType == OudsPinCodeInputKeyboardType.numeric + ? TextInputType.number + : TextInputType.text, + inputFormatters: widget.digitInputDecoration!.keyboardType == OudsPinCodeInputKeyboardType.numeric + ? [FilteringTextInputFormatter.digitsOnly] + : null, textAlign: TextAlign.center, maxLines: 1, buildCounter: (_, {required currentLength, required isFocused, required maxLength}) => null, // to hide the counter diff --git a/ouds_core/lib/components/pin_code_input/ouds_pin_code_input.dart b/ouds_core/lib/components/pin_code_input/ouds_pin_code_input.dart index b09b2eec8..9901ca57e 100644 --- a/ouds_core/lib/components/pin_code_input/ouds_pin_code_input.dart +++ b/ouds_core/lib/components/pin_code_input/ouds_pin_code_input.dart @@ -39,6 +39,19 @@ enum OudsPinCodeInputLength { const OudsPinCodeInputLength(); } +/// The [OudsPinCodeInputKeyboardType] defines which soft keyboard the digit cells request. +/// +/// - [numeric]: numeric keyboard (digits 0–9 only). Non-digit input is filtered out, including paste. +/// - [alphanumeric]: standard text keyboard. Any character is accepted. +/// +/// Defaults to [numeric] to match the historical PIN behavior. +enum OudsPinCodeInputKeyboardType { + numeric, + alphanumeric; + + const OudsPinCodeInputKeyboardType(); +} + /// [OUDS PIN Code Input Design Guidelines](https://r.orange.fr/r/S-ouds-doc-pin-code-input) /// /// **Reference design version : 1.2.0** @@ -215,6 +228,7 @@ class _OudsPinCodeInputState extends State { hintText: _hintText(index), hiddenPassword: widget.digitInputDecoration.hiddenPassword, isOutlined: widget.digitInputDecoration.isOutlined, + keyboardType: widget.digitInputDecoration.keyboardType, ), focusNode: _focusNodes[index], isHovered: _isHovered[index], @@ -304,7 +318,10 @@ class _OudsPinCodeInputState extends State { void _handlePaste(String value) { final totalDigits = widget.length.digits; final controllers = widget.controllers!; - final digits = value.characters.take(totalDigits).toList(); + final sanitized = widget.digitInputDecoration.keyboardType == OudsPinCodeInputKeyboardType.numeric + ? value.replaceAll(RegExp(r'\D'), '') + : value; + final digits = sanitized.characters.take(totalDigits).toList(); for (int i = 0; i < digits.length; i++) { controllers[i].text = digits[i];