Skip to content

Commit 0155127

Browse files
Merge pull request #329 from QueryaHub/issue/324-extension-table-view
feat(ui): upgrade ExtensionTableView with TableToolbar, custom SQL filter, and async count calculation
2 parents 630b942 + a8e683b commit 0155127

3 files changed

Lines changed: 486 additions & 63 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import 'package:flutter/material.dart' as material;
2+
import 'package:querya_desktop/shared/widgets/widgets.dart';
3+
4+
/// Standard toolbar for [ExtensionTableView] with title, pagination chip, DDL inspection, custom filter toggle, and navigation.
5+
class ExtensionTableToolbar extends material.StatelessWidget {
6+
const ExtensionTableToolbar({
7+
super.key,
8+
required this.title,
9+
required this.paginationLabel,
10+
required this.tableIcon,
11+
required this.loading,
12+
required this.canGoPrevious,
13+
required this.canGoNext,
14+
required this.filterActive,
15+
required this.filterText,
16+
required this.onToggleFilter,
17+
required this.onOpenDdl,
18+
required this.onGoPrevious,
19+
required this.onGoNext,
20+
required this.onRefresh,
21+
this.onCancelQuery,
22+
});
23+
24+
final String title;
25+
final String paginationLabel;
26+
final material.IconData tableIcon;
27+
final bool loading;
28+
final bool canGoPrevious;
29+
final bool canGoNext;
30+
final bool filterActive;
31+
final String filterText;
32+
final VoidCallback onToggleFilter;
33+
final VoidCallback onOpenDdl;
34+
final VoidCallback onGoPrevious;
35+
final VoidCallback onGoNext;
36+
final VoidCallback onRefresh;
37+
final VoidCallback? onCancelQuery;
38+
39+
@override
40+
material.Widget build(material.BuildContext context) {
41+
final cs = Theme.of(context).colorScheme;
42+
final isFiltered = filterActive || filterText.trim().isNotEmpty;
43+
44+
return material.Container(
45+
padding: const material.EdgeInsets.fromLTRB(16, 10, 16, 10),
46+
decoration: material.BoxDecoration(
47+
color: cs.card,
48+
border: material.Border(
49+
bottom: material.BorderSide(
50+
color: cs.border.withValues(alpha: 0.5),
51+
),
52+
),
53+
),
54+
child: material.Row(
55+
children: [
56+
material.Icon(tableIcon, size: 18, color: cs.primary),
57+
const Gap(8),
58+
material.Expanded(
59+
child: material.Text(
60+
title,
61+
style: material.TextStyle(
62+
fontSize: 13,
63+
fontWeight: material.FontWeight.w600,
64+
color: cs.foreground,
65+
),
66+
overflow: material.TextOverflow.ellipsis,
67+
maxLines: 1,
68+
),
69+
),
70+
material.Expanded(
71+
flex: 2,
72+
child: material.LayoutBuilder(
73+
builder: (context, constraints) {
74+
return material.SingleChildScrollView(
75+
scrollDirection: material.Axis.horizontal,
76+
child: material.ConstrainedBox(
77+
constraints: material.BoxConstraints(
78+
minWidth: constraints.maxWidth,
79+
),
80+
child: material.Row(
81+
mainAxisAlignment: material.MainAxisAlignment.end,
82+
mainAxisSize: material.MainAxisSize.min,
83+
children: [
84+
material.Container(
85+
padding: const material.EdgeInsets.symmetric(
86+
horizontal: 8,
87+
vertical: 3,
88+
),
89+
decoration: material.BoxDecoration(
90+
color: cs.muted.withValues(alpha: 0.4),
91+
borderRadius: material.BorderRadius.circular(4),
92+
),
93+
child: material.Text(
94+
paginationLabel,
95+
style: material.TextStyle(
96+
fontSize: 11,
97+
color: cs.mutedForeground,
98+
),
99+
),
100+
),
101+
const Gap(6),
102+
OutlineButton(
103+
size: ButtonSize.small,
104+
onPressed: onOpenDdl,
105+
leading: const material.Icon(
106+
material.Icons.code_rounded,
107+
size: 16,
108+
),
109+
child: const Text('DDL'),
110+
),
111+
const Gap(4),
112+
OutlineButton(
113+
size: ButtonSize.small,
114+
onPressed: onToggleFilter,
115+
leading: material.Icon(
116+
isFiltered
117+
? material.Icons.filter_alt_rounded
118+
: material.Icons.filter_alt_outlined,
119+
size: 15,
120+
),
121+
child: Text(isFiltered ? 'Filter (active)' : 'Filter'),
122+
),
123+
if (loading && onCancelQuery != null) ...[
124+
const Gap(4),
125+
OutlineButton(
126+
size: ButtonSize.small,
127+
onPressed: onCancelQuery,
128+
leading: const material.Icon(
129+
material.Icons.cancel_outlined,
130+
size: 15,
131+
),
132+
child: const Text('Cancel'),
133+
),
134+
],
135+
const Gap(4),
136+
OutlineButton(
137+
size: ButtonSize.small,
138+
onPressed: canGoPrevious ? onGoPrevious : null,
139+
leading: const material.Icon(
140+
material.Icons.chevron_left_rounded,
141+
size: 16,
142+
),
143+
child: const Text('Back'),
144+
),
145+
const Gap(4),
146+
OutlineButton(
147+
size: ButtonSize.small,
148+
onPressed: canGoNext ? onGoNext : null,
149+
leading: const material.Icon(
150+
material.Icons.chevron_right_rounded,
151+
size: 16,
152+
),
153+
child: const Text('Next'),
154+
),
155+
const Gap(8),
156+
OutlineButton(
157+
size: ButtonSize.small,
158+
onPressed: loading ? null : onRefresh,
159+
leading: const material.Icon(
160+
material.Icons.refresh_rounded,
161+
size: 14,
162+
),
163+
child: const Text('Refresh'),
164+
),
165+
],
166+
),
167+
),
168+
);
169+
},
170+
),
171+
),
172+
],
173+
),
174+
);
175+
}
176+
}

0 commit comments

Comments
 (0)