From 170bfff3b42b656a774e947bdc70bf0b4fd092e0 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 11 Jan 2023 10:23:45 +0700 Subject: [PATCH] TF-1354 Fix overflow text on web --- .../presentation/composer_view_web.dart | 35 ++- .../widgets/email_address_input_builder.dart | 218 ++++++++++++------ ...complete_contact_text_field_with_tags.dart | 22 +- .../widgets/contact_input_tag_item.dart | 40 +++- pubspec.lock | 10 +- pubspec.yaml | 6 +- 6 files changed, 233 insertions(+), 98 deletions(-) diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 802e08e70..40b25fa70 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -418,15 +418,23 @@ class ComposerView extends GetWidget children: [ Text( item.name ?? '', - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.normal, color: Colors.black), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.normal, + color: Colors.black), maxLines: 1, - overflow: TextOverflow.ellipsis, + overflow: CommonTextStyle.defaultTextOverFlow, + softWrap: CommonTextStyle.defaultSoftWrap ), Text( item.email ?? '', - style: const TextStyle(fontSize: 13, fontWeight: FontWeight.normal, color: AppColor.colorHintSearchBar), + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColor.colorHintSearchBar), maxLines: 1, - overflow: TextOverflow.ellipsis, + overflow: CommonTextStyle.defaultTextOverFlow, + softWrap: CommonTextStyle.defaultSoftWrap ) ] ), @@ -447,13 +455,18 @@ class ComposerView extends GetWidget ), ), Expanded(child: Padding( - padding: const EdgeInsets.only(right: 8, left: 12), - child: Text( - controller.identitySelected.value?.email ?? '', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 17, fontWeight: FontWeight.normal, color: AppColor.colorEmailAddressPrefix), - ))), + padding: const EdgeInsets.only(right: 8, left: 12), + child: Text( + controller.identitySelected.value?.email ?? '', + maxLines: 1, + overflow: CommonTextStyle.defaultTextOverFlow, + softWrap: CommonTextStyle.defaultSoftWrap, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.normal, + color: AppColor.colorEmailAddressPrefix), + ) + )), ]), ); } diff --git a/lib/features/composer/presentation/widgets/email_address_input_builder.dart b/lib/features/composer/presentation/widgets/email_address_input_builder.dart index efc9dc9b9..12bf6981d 100644 --- a/lib/features/composer/presentation/widgets/email_address_input_builder.dart +++ b/lib/features/composer/presentation/widgets/email_address_input_builder.dart @@ -45,6 +45,7 @@ class EmailAddressInputBuilder { OnFocusEmailAddressChangeAction? _onFocusEmailAddressChangeAction; Timer? _gapBetweenTagChangedAndFindSuggestion; + bool lastTagFocused = false; void addOnUpdateListEmailAddressAction(OnUpdateListEmailAddressAction onUpdateListEmailAddressAction) { _onUpdateListEmailAddressAction = onUpdateListEmailAddressAction; @@ -148,7 +149,12 @@ class EmailAddressInputBuilder { suggestionsBoxRadius: 20, suggestionsBoxMaxHeight: 300, textStyle: const TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500), - onDeleteTagAction: () => _handleDeleteTagAction(setState), + onFocusTagAction: (focused) { + setState(() { + lastTagFocused = focused; + }); + }, + onDeleteTagAction: () => _handleDeleteTagAction(setState, context), onSelectOptionAction: (item) { if (!_isDuplicatedRecipient(item.emailAddress.emailAddress)) { setState(() => listEmailAddress.add(item.emailAddress)); @@ -163,39 +169,56 @@ class EmailAddressInputBuilder { } }, inputDecoration: const InputDecoration(border: InputBorder.none), - tagBuilder: (context, index) => Stack(alignment: Alignment.centerRight, children: [ - Padding( - padding: EdgeInsets.only(top: kIsWeb ? 8 : 0, right: _isCollapse ? 50 : 0), - child: InkWell( - onTap: () => _isCollapse ? _onShowFullListEmailAddressAction?.call(_prefixEmailAddress) : null, + tagBuilder: (context, index) { + final isLastEmail = index == listEmailAddress.length - 1; + return Stack( + alignment: Alignment.centerRight, + children: [ + Padding( + padding: EdgeInsets.only( + top: kIsWeb ? 8 : 0, + right: _isCollapse ? 50 : 0), + child: InkWell( + onTap: () => _isCollapse + ? _onShowFullListEmailAddressAction?.call(_prefixEmailAddress) + : null, child: Chip( labelPadding: const EdgeInsets.only(left: 12, right: 12, bottom: 2), label: Text(newListEmailAddress[index].asString(), maxLines: 1, overflow: kIsWeb ? null : TextOverflow.ellipsis), deleteIcon: SvgPicture.asset(_imagePaths.icClose, fit: BoxFit.fill), labelStyle: const TextStyle(color: Colors.black, fontSize: 17, fontWeight: FontWeight.normal), - backgroundColor: _isEmailAddressValid(listEmailAddress[index].emailAddress) ? AppColor.colorEmailAddressTag : Colors.white, + backgroundColor: _getTagBackgroundColor(listEmailAddress[index], isLastEmail), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), - side: BorderSide( - width: _isEmailAddressValid(listEmailAddress[index].emailAddress) ? 0 : 1, - color: _isEmailAddressValid(listEmailAddress[index].emailAddress) ? AppColor.colorEmailAddressTag : AppColor.colorBorderEmailAddressInvalid), + side: _getTagBorderSide(listEmailAddress[index], isLastEmail), ), avatar: newListEmailAddress[index].displayName.isNotEmpty - ? CircleAvatar( - backgroundColor: AppColor.colorTextButton, - child: Text( - listEmailAddress[index].displayName[0].toUpperCase(), - style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500))) - : null, + ? CircleAvatar( + backgroundColor: AppColor.colorTextButton, + child: Text( + listEmailAddress[index].displayName[0].toUpperCase(), + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w500 + ) + )) + : null, onDeleted: () { setState(() => listEmailAddress.removeAt(index)); _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, listEmailAddress); }, ) - ) - ), - if (_isCollapse) _buildCounter(context, listEmailAddress.length - newListEmailAddress.length), - ]), + ) + ), + if (_isCollapse) + _buildCounter( + context, + listEmailAddress.length - newListEmailAddress.length + ), + ] + ); + }, onTagChanged: (String value) { log('EmailAddressInputBuilder::_buildTagEditor(): onTagChanged: $value'); if (!_isDuplicatedRecipient(value)) { @@ -207,12 +230,25 @@ class EmailAddressInputBuilder { _handleGapBetweenTagChangedAndFindSuggestion); }, findSuggestions: _findSuggestions, - suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, highlight) { + useDefaultHighlight: false, + suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight) { switch (suggestionEmailAddress.state) { case SuggestionEmailState.duplicated: - return _buildExistedSuggestionItem(setState, context, suggestionEmailAddress); + return _buildExistedSuggestionItem( + setState, + context, + suggestionEmailAddress, + ); default: - return _buildSuggestionItem(setState, context, tagEditorState, suggestionEmailAddress); + return _buildSuggestionItem( + setState, + context, + tagEditorState, + suggestionEmailAddress, + index, + length, + highlight + ); } }, ) @@ -291,42 +327,71 @@ class EmailAddressInputBuilder { ); } - Widget _buildSuggestionItem(StateSetter setState, BuildContext context, TagsEditorState tagEditorState, SuggestionEmailAddress suggestionEmailAddress) { - return ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - leading: Container( - width: 40, - height: 40, - alignment: Alignment.center, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: AppColor.avatarColor, - border: Border.all(color: AppColor.colorShadowBgContentEmail, width: 1.0)), - child: Text( - suggestionEmailAddress.emailAddress.asString().isNotEmpty ? suggestionEmailAddress.emailAddress.asString()[0].toUpperCase() : '', - style: const TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.w600))), - title: Text( - suggestionEmailAddress.emailAddress.asString(), - maxLines: 1, - overflow: kIsWeb ? null : TextOverflow.ellipsis, - style: const TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.normal)), - subtitle: suggestionEmailAddress.emailAddress.displayName.isNotEmpty && suggestionEmailAddress.emailAddress.emailAddress.isNotEmpty - ? Text( - suggestionEmailAddress.emailAddress.emailAddress, - maxLines: 1, - overflow: kIsWeb ? null : TextOverflow.ellipsis, - style: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 13, fontWeight: FontWeight.normal)) - : null, - onTap: () { - log('EmailAddressInputBuilder::_buildSuggestionItem(): onTap: $suggestionEmailAddress'); - setState(() => listEmailAddress.add(suggestionEmailAddress.emailAddress)); - _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, listEmailAddress); - tagEditorState.closeSuggestionBox(); - tagEditorState.resetTextField(); - }, + Widget _buildSuggestionItem( + StateSetter setState, + BuildContext context, + TagsEditorState tagEditorState, + SuggestionEmailAddress suggestionEmailAddress, + int index, + int length, + bool highlight + ) { + return Container( + decoration: BoxDecoration( + color: highlight ? AppColor.colorItemSelected : Colors.white, + borderRadius: _getBorderRadiusSuggestionItem(index, length)), + child: ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + leading: Container( + width: 40, + height: 40, + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColor.avatarColor, + border: Border.all(color: AppColor.colorShadowBgContentEmail, width: 1.0)), + child: Text( + suggestionEmailAddress.emailAddress.asString().isNotEmpty ? suggestionEmailAddress.emailAddress.asString()[0].toUpperCase() : '', + style: const TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.w600))), + title: Text( + suggestionEmailAddress.emailAddress.asString(), + maxLines: 1, + overflow: kIsWeb ? null : TextOverflow.ellipsis, + style: const TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.normal)), + subtitle: suggestionEmailAddress.emailAddress.displayName.isNotEmpty && suggestionEmailAddress.emailAddress.emailAddress.isNotEmpty + ? Text( + suggestionEmailAddress.emailAddress.emailAddress, + maxLines: 1, + overflow: kIsWeb ? null : TextOverflow.ellipsis, + style: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 13, fontWeight: FontWeight.normal)) + : null, + onTap: () { + log('EmailAddressInputBuilder::_buildSuggestionItem(): onTap: $suggestionEmailAddress'); + setState(() => listEmailAddress.add(suggestionEmailAddress.emailAddress)); + _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, listEmailAddress); + tagEditorState.closeSuggestionBox(); + tagEditorState.resetTextField(); + }, + ), ); } + BorderRadius _getBorderRadiusSuggestionItem(int index, int length) { + var borderRadius = const BorderRadius.all(Radius.circular(0)); + if (index == 0) { + borderRadius = const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ); + } else if (index == length - 1) { + borderRadius = const BorderRadius.only( + bottomRight: Radius.circular(20), + bottomLeft: Radius.circular(20), + ); + } + return borderRadius; + } + FutureOr> _findSuggestions(String query) async { log('EmailAddressInputBuilder::_findSuggestions():query: $query'); if (_gapBetweenTagChangedAndFindSuggestion?.isActive ?? false) { @@ -385,24 +450,35 @@ class EmailAddressInputBuilder { log('EmailAddressInputBuilder::_handleGapBetweenTagChangedAndFindSuggestion(): Timeout'); } - void _handleDeleteTagAction(StateSetter setState) { + Color _getTagBackgroundColor(EmailAddress emailCurrent, bool isLastEmail) { + if (lastTagFocused && isLastEmail) { + return AppColor.colorItemRecipientSelected; + } else { + return _isEmailAddressValid(emailCurrent.emailAddress) + ? AppColor.colorEmailAddressTag + : Colors.white; + } + } + + BorderSide _getTagBorderSide(EmailAddress emailCurrent, bool isLastEmail) { + if (lastTagFocused && isLastEmail) { + return const BorderSide( + width: 1, + color: AppColor.primaryColor); + } else { + return BorderSide( + width: _isEmailAddressValid(emailCurrent.emailAddress) ? 0 : 1, + color: _isEmailAddressValid(emailCurrent.emailAddress) + ? AppColor.colorEmailAddressTag + : AppColor.colorBorderEmailAddressInvalid); + } + } + + void _handleDeleteTagAction(StateSetter setState, BuildContext context) { log('EmailAddressInputBuilder::_handleDeleteTagAction()'); if (listEmailAddress.isNotEmpty) { setState(() { - final emailAddressDeleted = listEmailAddress.removeLast(); - - Future.delayed(const Duration(milliseconds: 10), () { - if (controller != null) { - controller!.text = emailAddressDeleted.emailAddress; - controller!.value = controller!.value.copyWith( - text: emailAddressDeleted.asString(), - selection: TextSelection( - baseOffset: emailAddressDeleted.asString().length, - extentOffset: emailAddressDeleted.asString().length - ) - ); - } - }); + listEmailAddress.removeLast(); }); _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, listEmailAddress); } diff --git a/lib/features/contact/presentation/widgets/autocomplete_contact_text_field_with_tags.dart b/lib/features/contact/presentation/widgets/autocomplete_contact_text_field_with_tags.dart index 2c9f9ddf3..aed69105e 100644 --- a/lib/features/contact/presentation/widgets/autocomplete_contact_text_field_with_tags.dart +++ b/lib/features/contact/presentation/widgets/autocomplete_contact_text_field_with_tags.dart @@ -53,6 +53,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State listEmailAddress; Timer? _gapBetweenTagChangedAndFindSuggestion; + bool lastTagFocused = false; @override void initState() { @@ -89,6 +90,23 @@ class _AutocompleteContactTextFieldWithTagsState extends State listEmailAddress.add(item.emailAddress)); + } + }, onSubmitted: (value) { if (!_isDuplicatedRecipient(value)) { setState(() => listEmailAddress.add(EmailAddress(null, value))); @@ -109,6 +127,8 @@ class _AutocompleteContactTextFieldWithTagsState extends State ContactInputTagItem( listEmailAddress[index], + isLastContact: index == listEmailAddress.length - 1, + lastTagFocused: lastTagFocused, deleteContactCallbackAction: (contact) { setState(() => listEmailAddress.remove(contact)); } @@ -123,7 +143,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State deleteContactCallbackAction?.call(contact), ); @@ -88,4 +85,29 @@ class ContactInputTagItem extends StatelessWidget { } bool _isValidEmailAddress(String value) => value.isEmail; + + Color _getTagBackgroundColor() { + if (lastTagFocused && isLastContact) { + return AppColor.colorItemRecipientSelected; + } else { + return _isValidEmailAddress(contact.emailAddress) + ? AppColor.colorBackgroundContactTagItem + : Colors.white; + } + } + + BorderSide _getTagBorderSide() { + if (lastTagFocused && isLastContact) { + return const BorderSide( + width: 1, + color: AppColor.primaryColor); + } else { + return BorderSide( + width: _isValidEmailAddress(contact.emailAddress) ? 0 : 1, + color: _isValidEmailAddress(contact.emailAddress) + ? AppColor.colorBackgroundContactTagItem + : AppColor.colorBorderEmailAddressInvalid + ); + } + } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 81e80aad6..f7bdf573a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -871,7 +871,7 @@ packages: description: path: "." ref: master - resolved-ref: b848fdda3c48c78594a804bb609e1077bb42125d + resolved-ref: "92982e13c9414c71e14451da83668b05c282fc2b" url: "https://github.com/linagora/jmap-dart-client.git" source: git version: "0.0.1" @@ -1377,9 +1377,11 @@ packages: super_tag_editor: dependency: "direct main" description: - name: super_tag_editor - url: "https://pub.dartlang.org" - source: hosted + path: "." + ref: master + resolved-ref: "34ffe7c8c5e8a967ae1cfde2a525c4f5738a0835" + url: "https://github.com/dab246/super_tag_editor.git" + source: git version: "0.0.3+4" syncfusion_flutter_core: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index 23e9912fd..f44b95956 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -99,8 +99,10 @@ dependencies: filesize: 2.0.1 # super_tag_editor - super_tag_editor: 0.0.3+4 - + super_tag_editor: + git: + url: https://github.com/dab246/super_tag_editor.git + ref: master # uuid uuid: 3.0.4