Skip to content

Commit c71c7a6

Browse files
committed
feat(reports): replace filter dropdowns with Filters modal button
- Single 'Filters' button with filter icon replaces cramped GPU/Architecture/Config dropdown row - Active filter count shown next to button (e.g. 'Filters (2)') when any filter is non-default - ReportFiltersModal gives each filter its own labeled row with full-width dropdown - Sort dropdown stays in the toolbar row outside the modal Closes #79
1 parent 75596be commit c71c7a6

1 file changed

Lines changed: 106 additions & 50 deletions

File tree

src/components/tabs/ConfigureTab.tsx

Lines changed: 106 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,82 @@ function findClosestInstalledTool(
232232
return ranked[0]?.tool ?? null;
233233
}
234234

235+
function ReportFiltersModal({
236+
filter, setFilterMode, gpuFilterCounts,
237+
archFilter, setArchFilter, availArchitectures, tierFiltered,
238+
configFilter, setConfigFilterMode, configFilterCounts,
239+
visibleReports,
240+
onClose,
241+
}: {
242+
filter: FilterTier;
243+
setFilterMode: (f: FilterTier) => void;
244+
gpuFilterCounts: Record<FilterTier, number>;
245+
archFilter: FilterArch;
246+
setArchFilter: (a: FilterArch) => void;
247+
availArchitectures: string[];
248+
tierFiltered: DisplayReportCard[];
249+
configFilter: ConfigFilter;
250+
setConfigFilterMode: (f: ConfigFilter) => void;
251+
configFilterCounts: Record<ConfigFilter, number>;
252+
visibleReports: DisplayReportCard[];
253+
onClose: () => void;
254+
}) {
255+
const rowStyle: React.CSSProperties = { display: 'flex', alignItems: 'center', gap: 10 };
256+
const labelStyle: React.CSSProperties = { fontSize: 11, color: '#cfe2f4', fontWeight: 700, width: 100, flexShrink: 0 };
257+
return (
258+
<ConfirmModal
259+
strTitle="Filters"
260+
strOKButtonText={t().common.close}
261+
onOK={onClose}
262+
onCancel={onClose}
263+
>
264+
<div style={{ display: 'flex', flexDirection: 'column', gap: 14, minWidth: 360 }}>
265+
<div style={rowStyle}>
266+
<div style={labelStyle}>{t().configManager.gpuFilter}</div>
267+
<div style={{ flex: 1 }}>
268+
<Dropdown
269+
strDefaultLabel={gpuFilterOptionLabel(filter, gpuFilterCounts[filter])}
270+
rgOptions={FILTER_ORDER.map((tier) => ({ data: tier, label: gpuFilterOptionLabel(tier, gpuFilterCounts[tier]) }))}
271+
selectedOption={filter}
272+
onChange={(opt) => setFilterMode(opt.data as FilterTier)}
273+
/>
274+
</div>
275+
</div>
276+
{availArchitectures.length > 1 && (
277+
<div style={rowStyle}>
278+
<div style={labelStyle}>Architecture</div>
279+
<div style={{ flex: 1 }}>
280+
<Dropdown
281+
strDefaultLabel={archFilter === 'all' ? `All (${tierFiltered.length})` : `${archFilter} (${visibleReports.length})`}
282+
rgOptions={[
283+
{ data: 'all', label: `All (${tierFiltered.length})` },
284+
...availArchitectures.map(arch => ({
285+
data: arch,
286+
label: `${arch} (${tierFiltered.filter(r => r.gpuArchitecture === arch).length})`,
287+
})),
288+
]}
289+
selectedOption={archFilter}
290+
onChange={(opt) => setArchFilter(opt.data as FilterArch)}
291+
/>
292+
</div>
293+
</div>
294+
)}
295+
<div style={rowStyle}>
296+
<div style={labelStyle}>{t().configManager.configFilter}</div>
297+
<div style={{ flex: 1 }}>
298+
<Dropdown
299+
strDefaultLabel={configFilterOptionLabel(configFilter, configFilterCounts[configFilter])}
300+
rgOptions={CONFIG_FILTER_ORDER.map((value) => ({ data: value, label: configFilterOptionLabel(value, configFilterCounts[value]) }))}
301+
selectedOption={configFilter}
302+
onChange={(opt) => setConfigFilterMode(opt.data as ConfigFilter)}
303+
/>
304+
</div>
305+
</div>
306+
</div>
307+
</ConfirmModal>
308+
);
309+
}
310+
235311
function MissingVersionModal({
236312
requiredVersion,
237313
latestInstalledLabel,
@@ -1377,7 +1453,6 @@ function ConfigureTabContent({ appId, appName, sysInfo }: Props) {
13771453
{t().common.shown(sortedReports.length)}
13781454
</div>
13791455
</div>
1380-
{/* flow-children="horizontal" makes dpad left/right move between the dropdowns */}
13811456
<Focusable
13821457
flow-children="horizontal"
13831458
style={{
@@ -1390,9 +1465,7 @@ function ConfigureTabContent({ appId, appName, sysInfo }: Props) {
13901465
borderBottom: '1px solid #2a3a4a',
13911466
}}
13921467
>
1393-
<div
1394-
style={{ fontSize: 10, color: '#cfe2f4', fontWeight: 700, whiteSpace: 'nowrap', textAlign: 'right' }}
1395-
>
1468+
<div style={{ fontSize: 10, color: '#cfe2f4', fontWeight: 700, whiteSpace: 'nowrap' }}>
13961469
{t().common.sort}
13971470
</div>
13981471
<Dropdown
@@ -1404,53 +1477,36 @@ function ConfigureTabContent({ appId, appName, sysInfo }: Props) {
14041477
selectedOption={sortMode}
14051478
onChange={(opt) => setSortPreference(opt.data as SortMode)}
14061479
/>
1407-
<div
1408-
style={{ fontSize: 10, color: '#cfe2f4', fontWeight: 700, whiteSpace: 'nowrap', textAlign: 'right' }}
1409-
>
1410-
{t().configManager.gpuFilter}
1411-
</div>
1412-
<Dropdown
1413-
strDefaultLabel={gpuFilterOptionLabel(filter, gpuFilterCounts[filter])}
1414-
rgOptions={FILTER_ORDER.map((tier) => ({
1415-
data: tier,
1416-
label: gpuFilterOptionLabel(tier, gpuFilterCounts[tier]),
1417-
}))}
1418-
selectedOption={filter}
1419-
onChange={(opt) => setFilterMode(opt.data as FilterTier)}
1420-
/>
1421-
{availArchitectures.length > 1 && (<>
1422-
<div
1423-
style={{ fontSize: 10, color: '#cfe2f4', fontWeight: 700, whiteSpace: 'nowrap', textAlign: 'right' }}
1424-
>
1425-
Architecture
1426-
</div>
1427-
<Dropdown
1428-
strDefaultLabel={archFilter === 'all' ? `All (${tierFiltered.length})` : `${archFilter} (${visibleReports.length})`}
1429-
rgOptions={[
1430-
{ data: 'all', label: `All (${tierFiltered.length})` },
1431-
...availArchitectures.map(arch => ({
1432-
data: arch,
1433-
label: `${arch} (${tierFiltered.filter(r => r.gpuArchitecture === arch).length})`,
1434-
})),
1435-
]}
1436-
selectedOption={archFilter}
1437-
onChange={(opt) => setArchFilter(opt.data as FilterArch)}
1438-
/>
1439-
</>)}
1440-
<div
1441-
style={{ fontSize: 10, color: '#cfe2f4', fontWeight: 700, whiteSpace: 'nowrap', textAlign: 'right' }}
1480+
<DialogButton
1481+
style={{ minWidth: 0, padding: '4px 12px', display: 'flex', alignItems: 'center', gap: 6 }}
1482+
onClick={() => {
1483+
const modal = showModal(
1484+
<ReportFiltersModal
1485+
filter={filter}
1486+
setFilterMode={setFilterMode}
1487+
gpuFilterCounts={gpuFilterCounts}
1488+
archFilter={archFilter}
1489+
setArchFilter={setArchFilter}
1490+
availArchitectures={availArchitectures}
1491+
tierFiltered={tierFiltered}
1492+
configFilter={configFilter}
1493+
setConfigFilterMode={setConfigFilterMode}
1494+
configFilterCounts={configFilterCounts}
1495+
visibleReports={visibleReports}
1496+
onClose={() => modal.Close()}
1497+
/>,
1498+
);
1499+
}}
14421500
>
1443-
{t().configManager.configFilter}
1444-
</div>
1445-
<Dropdown
1446-
strDefaultLabel={configFilterOptionLabel(configFilter, configFilterCounts[configFilter])}
1447-
rgOptions={CONFIG_FILTER_ORDER.map((value) => ({
1448-
data: value,
1449-
label: configFilterOptionLabel(value, configFilterCounts[value]),
1450-
}))}
1451-
selectedOption={configFilter}
1452-
onChange={(opt) => setConfigFilterMode(opt.data as ConfigFilter)}
1453-
/>
1501+
{/* funnel icon */}
1502+
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
1503+
<path d="M10 18h4v-2h-4v2zm-7-12v2h18V6H3zm3 7h12v-2H6v2z"/>
1504+
</svg>
1505+
{(() => {
1506+
const count = [filter !== 'all', archFilter !== 'all', configFilter !== 'all'].filter(Boolean).length;
1507+
return count > 0 ? `Filters (${count})` : 'Filters';
1508+
})()}
1509+
</DialogButton>
14541510
</Focusable>
14551511
<div style={{ flex: 1, minHeight: 0, overflowY: 'auto', paddingRight: 4 }}>
14561512
<div style={{ marginBottom: 12, color: '#9db0c4', fontSize: 11 }}>

0 commit comments

Comments
 (0)