74 lines
2.2 KiB
Dart
74 lines
2.2 KiB
Dart
import 'package:flutter/services.dart';
|
||
|
||
/// Formatage affichage prénom / nom (capitalisation par mot et segments après `-` ou `'`).
|
||
|
||
String formatPersonNameCase(String raw) {
|
||
final trimmed = raw.trim();
|
||
if (trimmed.isEmpty) {
|
||
return trimmed;
|
||
}
|
||
final words = trimmed.split(RegExp(r'\s+'));
|
||
return words.map(_capitalizeComposedWord).join(' ');
|
||
}
|
||
|
||
/// Variante saisie live : pas de majuscule tant que le mot n’a qu’une lettre ;
|
||
/// dès la 2ᵉ lettre, capitalisation comme [formatPersonNameCase].
|
||
String formatPersonNameCaseTyping(String raw) {
|
||
if (raw.isEmpty) return raw;
|
||
final trailingMatch = RegExp(r'(\s*)$').firstMatch(raw);
|
||
final trailing = trailingMatch?.group(1) ?? '';
|
||
final core = raw.substring(0, raw.length - trailing.length);
|
||
if (core.isEmpty) return raw;
|
||
final words = core.split(RegExp(r'\s+'));
|
||
final formatted = words.map((w) {
|
||
if (w.length < 2) return w;
|
||
return _capitalizeComposedWord(w);
|
||
}).join(' ');
|
||
return formatted + trailing;
|
||
}
|
||
|
||
String _capitalizeComposedWord(String word) {
|
||
if (word.isEmpty) {
|
||
return word;
|
||
}
|
||
final lower = word.toLowerCase();
|
||
const separators = <String>{'-', "'", '’'};
|
||
final buffer = StringBuffer();
|
||
var capitalizeNext = true;
|
||
|
||
for (var i = 0; i < lower.length; i++) {
|
||
final char = lower[i];
|
||
if (capitalizeNext && RegExp(r'[a-zà-öø-ÿ]').hasMatch(char)) {
|
||
buffer.write(char.toUpperCase());
|
||
capitalizeNext = false;
|
||
} else {
|
||
buffer.write(char);
|
||
capitalizeNext = separators.contains(char);
|
||
}
|
||
}
|
||
return buffer.toString();
|
||
}
|
||
|
||
/// Formateur de saisie prénom / nom (capitalisation progressive).
|
||
class PersonNameInputFormatter extends TextInputFormatter {
|
||
const PersonNameInputFormatter();
|
||
|
||
@override
|
||
TextEditingValue formatEditUpdate(
|
||
TextEditingValue oldValue,
|
||
TextEditingValue newValue,
|
||
) {
|
||
final formatted = formatPersonNameCaseTyping(newValue.text);
|
||
if (formatted == newValue.text) return newValue;
|
||
|
||
final sel = newValue.selection;
|
||
final offset = sel.isValid
|
||
? sel.baseOffset.clamp(0, formatted.length)
|
||
: formatted.length;
|
||
return TextEditingValue(
|
||
text: formatted,
|
||
selection: TextSelection.collapsed(offset: offset),
|
||
);
|
||
}
|
||
}
|