chore: improve rendering of vocab analytics popup, exclude empty lemmas, enable filtering
This commit is contained in:
parent
dc1b7f1f5a
commit
6d393f7745
5 changed files with 212 additions and 47 deletions
|
|
@ -34,8 +34,8 @@ class AnalyticsDetailsViewContent extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color textColor = (Theme.of(context).brightness != Brightness.light
|
||||
? construct.lemmaCategory.color
|
||||
: construct.lemmaCategory.darkColor) as Color;
|
||||
? construct.lemmaCategory.color(context)
|
||||
: construct.lemmaCategory.darkColor(context));
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ class LemmaUsageDots extends StatelessWidget {
|
|||
}
|
||||
|
||||
final Color textColor = (Theme.of(context).brightness != Brightness.light
|
||||
? construct.lemmaCategory.color
|
||||
: construct.lemmaCategory.darkColor) as Color;
|
||||
? construct.lemmaCategory.color(context)
|
||||
: construct.lemmaCategory.darkColor(context));
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ class VocabDetailsView extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color textColor = (Theme.of(context).brightness != Brightness.light
|
||||
? _construct.lemmaCategory.color
|
||||
: _construct.lemmaCategory.darkColor) as Color;
|
||||
? _construct.lemmaCategory.color(context)
|
||||
: _construct.lemmaCategory.darkColor(context));
|
||||
|
||||
return AnalyticsDetailsViewContent(
|
||||
title: Row(
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ class VocabAnalyticsListTileState extends State<VocabAnalyticsListTile> {
|
|||
padding: EdgeInsets.all(padding),
|
||||
decoration: BoxDecoration(
|
||||
color: _isHovered
|
||||
? widget.constructUse.constructLevel.color(context)
|
||||
? widget.constructUse.constructLevel
|
||||
.color(context)
|
||||
.withAlpha(20)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
|
|
@ -59,7 +61,7 @@ class VocabAnalyticsListTileState extends State<VocabAnalyticsListTile> {
|
|||
fontSize: 22,
|
||||
),
|
||||
)
|
||||
: widget.constructUse.constructLevel.icon(10),
|
||||
: widget.constructUse.constructLevel.icon(36.0),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_tile.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
|
||||
|
|
@ -11,59 +13,220 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||
|
||||
/// Displays vocab analytics, sorted into categories
|
||||
/// (flowers, greens, and seeds) by points
|
||||
class VocabAnalyticsListView extends StatelessWidget {
|
||||
class VocabAnalyticsListView extends StatefulWidget {
|
||||
final void Function(ConstructIdentifier) onConstructZoom;
|
||||
|
||||
List<ConstructUses> get vocab => MatrixState
|
||||
.pangeaController.getAnalytics.constructListModel
|
||||
.constructList(type: ConstructTypeEnum.vocab)
|
||||
..sort((a, b) => a.lemma.toLowerCase().compareTo(b.lemma.toLowerCase()));
|
||||
|
||||
const VocabAnalyticsListView({
|
||||
super.key,
|
||||
required this.onConstructZoom,
|
||||
});
|
||||
|
||||
@override
|
||||
VocabAnalyticsListViewState createState() => VocabAnalyticsListViewState();
|
||||
}
|
||||
|
||||
class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
|
||||
bool _isSearching = false;
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
ConstructLevelEnum? _selectedConstructLevel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_searchController.addListener(() {
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _setSelectedConstructLevel(ConstructLevelEnum level) {
|
||||
setState(() {
|
||||
_selectedConstructLevel = _selectedConstructLevel == level ? null : level;
|
||||
});
|
||||
}
|
||||
|
||||
void _toggleSearching() {
|
||||
setState(() {
|
||||
_isSearching = !_isSearching;
|
||||
_selectedConstructLevel = null;
|
||||
_searchController.clear();
|
||||
});
|
||||
}
|
||||
|
||||
List<ConstructUses> get _vocab => MatrixState
|
||||
.pangeaController.getAnalytics.constructListModel
|
||||
.constructList(type: ConstructTypeEnum.vocab)
|
||||
.sorted((a, b) => a.lemma.toLowerCase().compareTo(b.lemma.toLowerCase()));
|
||||
|
||||
List<ConstructUses> get _filteredVocab => _vocab
|
||||
.where(
|
||||
(use) =>
|
||||
use.lemma.isNotEmpty &&
|
||||
(_selectedConstructLevel == null
|
||||
? true
|
||||
: use.lemmaCategory == _selectedConstructLevel) &&
|
||||
(_isSearching
|
||||
? use.lemma
|
||||
.toLowerCase()
|
||||
.contains(_searchController.text.toLowerCase())
|
||||
: true),
|
||||
)
|
||||
.toList();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.analyticsVocabList,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
final List<Widget> filters = ConstructLevelEnum.values.reversed
|
||||
.map((constructLevelCategory) {
|
||||
final int count = _vocab
|
||||
.where((e) => e.lemmaCategory == constructLevelCategory)
|
||||
.length;
|
||||
return InkWell(
|
||||
onTap: () => _setSelectedConstructLevel(constructLevelCategory),
|
||||
customBorder: const CircleBorder(),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: _selectedConstructLevel == constructLevelCategory
|
||||
? constructLevelCategory.color(context).withAlpha(50)
|
||||
: null,
|
||||
),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Badge(
|
||||
label: Text(count.toString()),
|
||||
child: constructLevelCategory.icon(24),
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
.cast<Widget>()
|
||||
.toList();
|
||||
|
||||
filters.add(
|
||||
IconButton(
|
||||
icon: const Icon(Icons.search_outlined),
|
||||
onPressed: _toggleSearching,
|
||||
),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.analyticsVocabList,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: _isSearching ? 8.0 : 24.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 50,
|
||||
children: ConstructLevelEnum.values.reversed
|
||||
.map((constructLevelCategory) {
|
||||
final int count = vocab
|
||||
.where((e) => e.lemmaCategory == constructLevelCategory)
|
||||
.length;
|
||||
|
||||
return Badge(
|
||||
label: Text(count.toString()),
|
||||
child: constructLevelCategory.icon(24),
|
||||
);
|
||||
}).toList(),
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 225.0),
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, animation) => FadeTransition(
|
||||
opacity: animation,
|
||||
child: child,
|
||||
),
|
||||
child: _isSearching
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
key: const ValueKey('search'),
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
controller: _searchController,
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: 6.0,
|
||||
horizontal: 12.0,
|
||||
),
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: _toggleSearching,
|
||||
),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
key: const ValueKey('filters'),
|
||||
children: filters,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
runAlignment: WrapAlignment.start,
|
||||
children: vocab
|
||||
.map(
|
||||
(vocab) => VocabAnalyticsListTile(
|
||||
onTap: () => onConstructZoom(vocab.id),
|
||||
constructUse: vocab,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(32.0),
|
||||
// child: Row(
|
||||
// spacing: _isSearching ? 8.0 : 24.0,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: _isSearching
|
||||
// ? [
|
||||
// ConstrainedBox(
|
||||
// constraints: const BoxConstraints(maxWidth: 200),
|
||||
// child: TextField(
|
||||
// autofocus: true,
|
||||
// controller: _searchController,
|
||||
// decoration: const InputDecoration(
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// vertical: 6.0,
|
||||
// horizontal: 12.0,
|
||||
// ),
|
||||
// isDense: true,
|
||||
// border: OutlineInputBorder(),
|
||||
// ),
|
||||
// onChanged: (value) {
|
||||
// if (mounted) setState(() {});
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.close),
|
||||
// onPressed: _toggleSearching,
|
||||
// ),
|
||||
// ]
|
||||
// : filters,
|
||||
// ),
|
||||
// ),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 100.0,
|
||||
mainAxisExtent: 100.0,
|
||||
crossAxisSpacing: 8.0,
|
||||
mainAxisSpacing: 8.0,
|
||||
),
|
||||
itemCount: _filteredVocab.length,
|
||||
itemBuilder: (context, index) {
|
||||
final vocabItem = _filteredVocab[index];
|
||||
return VocabAnalyticsListTile(
|
||||
onTap: () => widget.onConstructZoom(vocabItem.id),
|
||||
constructUse: vocabItem,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue