diff --git a/CHANGELOG.md b/CHANGELOG.md index acd57e999..5acffab71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## (future) + +- Fixed `TreeView` selection state behavior for items that are not expanded ([#578](https://github.com/bdlukaa/fluent_ui/issues/578)) +- Added support for Romanian language + ## 4.0.3+1 - Update documentation diff --git a/README.md b/README.md index cc7bfbeee..1ec1d9763 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ FluentUI widgets currently supports out-of-the-box an wide number of languages, - Persian (@xmine64) - Polish (@madik7) - Portuguese (@bdlukaa) +- Romanian (@antoniocranga) - Russian (@raitonoberu) - Simplified Chinese (@zacksleo, @rk0cc) - Traditional Chinese (@zacksleo, @rk0cc) diff --git a/example/lib/screens/navigation/tree_view.dart b/example/lib/screens/navigation/tree_view.dart index ebad2e397..3a876dcb6 100644 --- a/example/lib/screens/navigation/tree_view.dart +++ b/example/lib/screens/navigation/tree_view.dart @@ -209,7 +209,10 @@ TreeView( value: 'tax_middle_years', children: [ TreeViewItem(content: const Text('2018'), value: "tax_2018"), - TreeViewItem(content: const Text('2019'), value: "tax_2019"), + TreeViewItem( + content: const Text('2019'), + value: "tax_2019", + selected: true), TreeViewItem(content: const Text('2020'), value: "tax_2020"), ], ), diff --git a/lib/l10n/generated/fluent_localizations.dart b/lib/l10n/generated/fluent_localizations.dart index 25df40f3b..2da64deee 100644 --- a/lib/l10n/generated/fluent_localizations.dart +++ b/lib/l10n/generated/fluent_localizations.dart @@ -22,6 +22,7 @@ import 'fluent_localizations_ms.dart'; import 'fluent_localizations_nl.dart'; import 'fluent_localizations_pl.dart'; import 'fluent_localizations_pt.dart'; +import 'fluent_localizations_ro.dart'; import 'fluent_localizations_ru.dart'; import 'fluent_localizations_tr.dart'; import 'fluent_localizations_uz.dart'; @@ -128,6 +129,7 @@ abstract class FluentLocalizations { Locale('nl'), Locale('pl'), Locale('pt'), + Locale('ro'), Locale('ru'), Locale('tr'), Locale('uz'), @@ -351,6 +353,7 @@ class _FluentLocalizationsDelegate 'nl', 'pl', 'pt', + 'ro', 'ru', 'tr', 'uz', @@ -410,6 +413,8 @@ FluentLocalizations lookupFluentLocalizations(Locale locale) { return FluentLocalizationsPl(); case 'pt': return FluentLocalizationsPt(); + case 'ro': + return FluentLocalizationsRo(); case 'ru': return FluentLocalizationsRu(); case 'tr': diff --git a/lib/l10n/generated/fluent_localizations_ro.dart b/lib/l10n/generated/fluent_localizations_ro.dart new file mode 100644 index 000000000..8b616a047 --- /dev/null +++ b/lib/l10n/generated/fluent_localizations_ro.dart @@ -0,0 +1,101 @@ +import 'fluent_localizations.dart'; + +/// The translations for Romanian Moldavian Moldovan (`ro`). +class FluentLocalizationsRo extends FluentLocalizations { + FluentLocalizationsRo([String locale = 'ro']) : super(locale); + + @override + String get backButtonTooltip => 'Înapoi'; + + @override + String get closeButtonLabel => 'închide'; + + @override + String get searchLabel => 'Caută'; + + @override + String get closeNavigationTooltip => 'Închide Navigația'; + + @override + String get openNavigationTooltip => 'Deschide Navigația'; + + @override + String get clickToSearch => 'Click pentru a caută'; + + @override + String get modalBarrierDismissLabel => 'Respingeți'; + + @override + String get minimizeWindowTooltip => 'Minimizează'; + + @override + String get restoreWindowTooltip => 'Restabilește'; + + @override + String get closeWindowTooltip => 'Închide'; + + @override + String get dialogLabel => 'Dialog'; + + @override + String get cutActionLabel => 'Taie'; + + @override + String get copyActionLabel => 'Copiază'; + + @override + String get pasteActionLabel => 'Lipește'; + + @override + String get selectAllActionLabel => 'Selectează totul'; + + @override + String get newTabLabel => 'Adăugați o filă nouă'; + + @override + String get closeTabLabelSuffix => 'Închide filă'; + + @override + String get scrollTabBackwardLabel => 'Derulați înapoi lista cu file'; + + @override + String get scrollTabForwardLabel => 'Derulați înainte lista cu file'; + + @override + String get noResultsFoundLabel => 'Nici un rezultat gasit'; + + @override + String get copyActionTooltip => 'Copiați conținutul selectat în clipboard'; + + @override + String get cutActionTooltip => + 'Eliminați conținutul selectat și puneți-l în clipboard'; + + @override + String get pasteActionTooltip => + 'Inserează conținutul clipboard-ului în locația curentă'; + + @override + String get selectAllActionTooltip => 'Selectați tot conținutul'; + + @override + String get hour => 'oră'; + + @override + String get minute => 'minut'; + + @override + String get am => 'AM'; + + @override + String get pm => 'PM'; + + @override + String get month => 'lună'; + + @override + String get day => 'zi'; + + @override + String get year => 'an'; +} diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb new file mode 100644 index 000000000..ffdff429c --- /dev/null +++ b/lib/l10n/intl_ro.arb @@ -0,0 +1,127 @@ +{ + "@@locale": "ro", + "backButtonTooltip": "Înapoi", + "@backButtonTooltip": { + "description": "Sfat pentru butonul de întoarcere [NavigationAppBar]." + }, + "closeButtonLabel": "închide", + "@closeButtonLabel": { + "description": "Etichetă pentru \"închide\" butoane și elementele meniului." + }, + "searchLabel": "Caută", + "@searchLabel": { + "description": "Etichetă pentru \"caută\" campurile de text." + }, + "closeNavigationTooltip": "Închide Navigația", + "@closeNavigationTooltip": { + "description": "Sfat pentru butonul de navigație." + }, + "openNavigationTooltip": "Deschide Navigația", + "@openNavigationTooltip": { + "description": "Sfat pentru butonul de navigație." + }, + "clickToSearch": "Click pentru a caută", + "@clickToSearch": { + "description": "Sfat pentru butonul \"Apasă pentru a căuta\"." + }, + "modalBarrierDismissLabel": "Respingeți", + "@modalBarrierDismissLabel": { + "description": "Eticheta citiți cu ajutorul instrumentelor de accesibilitate (TalkBack sau VoiceOver) pentru o barieră modală pentru a indica faptul că o atingere închide bariera. O barieră modală poate fi găsită, de exemplu, în spatele unei alerte sau a unei ferestre pop-up pentru a bloca interacțiunea utilizatorului cu elementele din spatele acesteia." + }, + "minimizeWindowTooltip": "Minimizează", + "@minimizeWindowTooltip": { + "description": "Eticheta folosită de către butonul \"Minimizează\" pe desktop." + }, + "restoreWindowTooltip": "Restabilește", + "@restoreWindowTooltip": { + "description": "Eticheta folosită de către butonul \"Restabilește\" pe desktop." + }, + "closeWindowTooltip": "Închide", + "@closeWindowTooltip": { + "description": "Eticheta folosită de către butonul \"Închide\" pe desktop." + }, + "dialogLabel": "Dialog", + "@dialogLabel": { + "description": "Eticheta dialogului." + }, + "cutActionLabel": "Taie", + "@cutActionLabel": { + "description": "Eticheta pentru acțiunea de a tăia." + }, + "copyActionLabel": "Copiază", + "@copyActionLabel": { + "description": "Eticheta pentru acțiunea de a copia." + }, + "pasteActionLabel": "Lipește", + "@pasteActionLabel": { + "description": "Eticheta pentru butonul de a lipi." + }, + "selectAllActionLabel": "Selectează totul", + "@selectAllActionLabel": { + "description": "Eticheta pentru butonul de a selecta totul." + }, + "newTabLabel": "Adăugați o filă nouă", + "@newTabLabel": { + "description": "Eticheta folosită de butonul adaugă al lui [TabView]." + }, + "closeTabLabelSuffix": "Închide filă", + "@closeTabLabelSuffix": { + "description": "Eticheta folosită de butonul închidere al lui [TabView]." + }, + "scrollTabBackwardLabel": "Derulați înapoi lista cu file", + "@scrollTabBackwardLabel": { + "description": "Eticheta folosită de butonul derulează înapoi al lui [TabView]." + }, + "scrollTabForwardLabel": "Derulați înainte lista cu file", + "@scrollTabForwardLabel": { + "description": "Eticheta folosită de butonul derulează înainte al lui [TabView]." + }, + "noResultsFoundLabel": "Nici un rezultat gasit", + "@noResultsFoundLabel": { + "description": "Eticheta folosită de [AutoSuggestBox] când rezultatele nu pot fi găsite." + }, + "copyActionTooltip": "Copiați conținutul selectat în clipboard", + "@copyActionTooltip": { + "description": "Sfat pentru acțiunea de copiere a controalelor de selecție ale textului." + }, + "cutActionTooltip": "Eliminați conținutul selectat și puneți-l în clipboard", + "@cutActionTooltip": { + "description": "Sfat pentru acțiunea de tăiere a controalelor de selecție ale textului." + }, + "pasteActionTooltip": "Inserează conținutul clipboard-ului în locația curentă", + "@pasteActionTooltip": { + "description": "Sfat pentru acțiunea de lipire pe controalele de selecție ale textului." + }, + "selectAllActionTooltip": "Selectați tot conținutul", + "@selectAllActionTooltip": { + "description": "Sfat pentru acțiunea selectați tot din controalele de selecție ale textului." + }, + "hour": "oră", + "@hour": { + "description": "Textul folosit de [TimePicker] pentru câmpul oră." + }, + "minute": "minut", + "@minute": { + "description": "Textul folosit by [TimePicker] pentru câmpul minut." + }, + "am": "AM", + "@am": { + "description": "Textul folosit de [TimePicker] pentru câmpul AM." + }, + "pm": "PM", + "@pm": { + "description": "Textul folosit de [TimePicker] pentru câmpul PM." + }, + "month": "lună", + "@month": { + "description": "Textul folosit de [DatePicker] pentru câmpul lună." + }, + "day": "zi", + "@day": { + "description": "Textul folosit de [DatePicker] pentru câmpul zi." + }, + "year": "an", + "@year": { + "description": "Textul folosit de [DatePicker] pentru câmpul an." + } +} diff --git a/lib/src/controls/navigation/tree_view.dart b/lib/src/controls/navigation/tree_view.dart index 938941699..85f2b8a01 100644 --- a/lib/src/controls/navigation/tree_view.dart +++ b/lib/src/controls/navigation/tree_view.dart @@ -268,15 +268,25 @@ class TreeViewItem with Diagnosticable { (p) => p?.updateSelected(deselectParentWhenChildrenDeselected)); } - /// Updates [selected] based on the [children]s' state. [selected] will not - /// be forced to false if `deselectParentWhenChildrenDeselected` is false and + /// Updates [selected] based on the direct [children]s' state. + /// [selected] will not be forced to false if + /// `deselectParentWhenChildrenDeselected` is false and /// either there are no children or all children are deselected. + /// + /// Since this only updates the state based on direct children, + /// you would normally only call this in a depth-first manner on + /// all parents, for example: + /// + /// ```dart + /// item.executeForAllParents((parent) => parent + /// ?.updateSelected(widget.deselectParentWhenChildrenDeselected)) + /// ``` void updateSelected(bool deselectParentWhenChildrenDeselected) { bool hasNull = false; bool hasFalse = false; bool hasTrue = false; - for (final child in children.build(assignInternalProperties: false)) { + for (final child in children) { if (child.selected == null) { hasNull = true; } else if (child.selected == false) { @@ -372,35 +382,26 @@ class TreeViewItem with Diagnosticable { extension TreeViewItemCollection on List { /// Adds the [TreeViewItem.parent] property to the [TreeViewItem]s /// and calculates other internal properties. - List build({ - TreeViewItem? parent, - bool assignInternalProperties = true, - }) { + List build({TreeViewItem? parent}) { if (isNotEmpty) { final List list = []; - final anyExpandableSiblings = - assignInternalProperties ? any((i) => i.isExpandable) : null; + final anyExpandableSiblings = any((i) => i.isExpandable); for (final item in [...this]) { - if (assignInternalProperties) { - item._parent = parent; - item._anyExpandableSiblings = anyExpandableSiblings!; - } + item._parent = parent; + item._anyExpandableSiblings = anyExpandableSiblings; if (parent != null) { item._visible = parent._visible; } if (item._visible) { list.add(item); } - final itemAnyExpandableSiblings = assignInternalProperties - ? item.children.any((i) => i.isExpandable) - : null; + final itemAnyExpandableSiblings = + item.children.any((i) => i.isExpandable); for (final child in item.children) { // only add the children when it's expanded and visible child._visible = item.expanded && item._visible; - if (assignInternalProperties) { - child._parent = item; - child._anyExpandableSiblings = itemAnyExpandableSiblings!; - } + child._parent = item; + child._anyExpandableSiblings = itemAnyExpandableSiblings; if (child._visible) { list.add(child); } @@ -632,7 +633,7 @@ class _TreeViewState extends State void buildItems() { if (widget.selectionMode != TreeViewSelectionMode.single) { items = widget.items.build(); - items.executeForAll( + widget.items.executeForAll( (item) => item.executeForAllParents((parent) => parent ?.updateSelected(widget.deselectParentWhenChildrenDeselected)), ); @@ -771,7 +772,7 @@ class _TreeViewState extends State setState(() { item.loading = false; item.expanded = !item.expanded; - items = widget.items.build(); + buildItems(); }); } },