Skip to content

Commit d66c81c

Browse files
authored
Various UI enhancements/fixes (#8307)
2 parents 9dceab6 + 0c0d55c commit d66c81c

File tree

10 files changed

+85
-17
lines changed

10 files changed

+85
-17
lines changed

src/tribler/ui/package-lock.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tribler/ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"react-router-dom": "^6.16.0",
4343
"tailwind-merge": "^1.14.0",
4444
"tailwindcss-animate": "^1.0.7",
45+
"use-keyboard-shortcut": "^1.1.6",
4546
"zod": "^3.22.4"
4647
},
4748
"devDependencies": {

src/tribler/ui/src/components/layouts/Header.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useEffect, useRef, useState } from "react";
1414
import toast, { Toaster } from 'react-hot-toast';
1515
import Cookies from "js-cookie";
1616
import { DialogDescription } from "@radix-ui/react-dialog";
17-
import { Ban } from "lucide-react";
17+
import { Ban, Loader } from "lucide-react";
1818
import { useTranslation } from "react-i18next";
1919
import { ScrollArea } from "../ui/scroll-area";
2020

@@ -84,7 +84,8 @@ export function Header() {
8484
}}
8585
>
8686
<DialogHeader>
87-
<DialogTitle className="flex items-center justify-center mb-3"><Ban className="inline mr-3" />
87+
<DialogTitle className="flex items-center justify-center mb-3">
88+
{online ? <Loader className="inline mr-3 animate-[spin_3s_linear_infinite]" /> : <Ban className="inline mr-3" />}
8889
{online
8990
? "Tribler is shutting down"
9091
: (shutdownLogs.length > 0

src/tribler/ui/src/components/layouts/SideLayout.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState } from "react";
2-
import { NavLink, Outlet, useLocation } from "react-router-dom";
2+
import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom";
33
import { Header } from "./Header";
44
import { Accordion } from "@radix-ui/react-accordion";
55
import { sideMenu } from "@/config/menu";
@@ -15,6 +15,8 @@ import { useTranslation } from "react-i18next";
1515
export function SideLayout() {
1616
const { t } = useTranslation();
1717
const location = useLocation();
18+
const navigate = useNavigate();
19+
const [history, setHistory] = useState(new Map());
1820
const [accordionValue, setAccordionValue] = useState(() => {
1921
return "item-" + sideMenu.findIndex(item => item.items !== undefined ? item.items.filter(subitem => subitem.to !== undefined).map(subitem => subitem.to).includes(location.pathname) : false)
2022
});
@@ -44,12 +46,19 @@ export function SideLayout() {
4446
{sideMenu.filter((item) => !item.hide || item.hide() !== true).map((item, index) => (
4547
item.items !== undefined ? (
4648
<AccordionItem key={index} value={`item-${index}`} className="border-b-0">
47-
<AccordionTrigger className={cn(
48-
buttonVariants({ variant: "ghost" }),
49-
(item.items.filter(subitem => subitem.to !== undefined).map(subitem => subitem.to))
50-
.includes(location.pathname) ? 'bg-accent' : 'hover:bg-accent',
51-
"justify-between hover:no-underline"
52-
)}>
49+
<AccordionTrigger
50+
className={cn(
51+
buttonVariants({ variant: "ghost" }),
52+
(item.items.filter(subitem => subitem.to !== undefined).map(subitem => subitem.to))
53+
.includes(location.pathname) ? 'bg-accent' : 'hover:bg-accent',
54+
"justify-between hover:no-underline"
55+
)}
56+
onClick={() => {
57+
const target = history.get(item.title) || item.items?.at(0)?.to;
58+
if (target) {
59+
navigate(target);
60+
}
61+
}}>
5362
<div className="flex items-center">{item.icon && <item.icon className="mr-2" />} {t(item.title)}</div>
5463
</AccordionTrigger>
5564
<AccordionContent className="pb-2 pl-6">
@@ -59,7 +68,13 @@ export function SideLayout() {
5968
<NavLink
6069
key={subindex}
6170
to={submenu.to}
62-
onClick={() => setShowNav(false)}
71+
onClick={() => {
72+
setShowNav(false);
73+
if (item.title && submenu.to) {
74+
// Keep track of which submenus we navigated to
75+
setHistory(map => new Map(map.set(item.title, submenu.to)));
76+
}
77+
}}
6378
className={({ isActive }) => cn(
6479
buttonVariants({ variant: "ghost" }),
6580
isActive ? "bg-accent" : "hover:bg-accent",

src/tribler/ui/src/components/ui/autocomplete.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { useRef, useState } from "react";
2+
import { Button } from "./button";
3+
import { SearchIcon } from "lucide-react";
24

35

46
export function Autocomplete({ placeholder, completions, onChange }: { placeholder: string, completions: (filter: string) => Promise<string[]>, onChange: (query: string) => void }) {
@@ -61,10 +63,23 @@ export function Autocomplete({ placeholder, completions, onChange }: { placehold
6163
value={inputValue}
6264
ref={inputRef}
6365
/>
66+
<Button
67+
variant="ghost"
68+
className="h-6 py-0 px-0
69+
hover:outline hover:outline-neutral-500 outline-1 outline-offset-1
70+
active:outline active:outline-neutral-900 dark:active:outline-neutral-200"
71+
onClick={() => {
72+
const query = (selectedSuggestion > 0) ? suggestions[selectedSuggestion - 1] : inputValue;
73+
handleSuggestionClick(query);
74+
inputRef.current?.blur();
75+
}}
76+
>
77+
<SearchIcon className="h-5" />
78+
</Button>
6479
</div>
6580
</div>
66-
<div className="relative mt-2">
67-
{focus && (
81+
{focus && suggestions.length > 0 &&(
82+
<div className="relative mt-2">
6883
<div className="max-h-[300px] overflow-y-auto overflow-x-hidden absolute top-0 z-10 w-full rounded-md border bg-popover text-popover-foreground shadow-md outline-none animate-in">
6984
{suggestions.length > 0 && (suggestions.map((suggestion, index) => (
7085
<div className={`p-1 text-foreground h-full overflow-auto ${(selectedSuggestion === index + 1) ? 'bg-accent' : ''}`} key={index + 'a'}>
@@ -77,10 +92,9 @@ export function Autocomplete({ placeholder, completions, onChange }: { placehold
7792
</div>
7893
)))}
7994
</div>
80-
)}
81-
</div>
95+
</div>
96+
)}
8297
</div>
8398
</div>
84-
8599
);
86100
}

src/tribler/ui/src/components/ui/simple-table.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as SelectPrimitive from "@radix-ui/react-select"
1010
import type { Table as ReactTable } from '@tanstack/react-table';
1111
import { useTranslation } from 'react-i18next';
1212
import { useResizeObserver } from '@/hooks/useResizeObserver';
13+
import useKeyboardShortcut from 'use-keyboard-shortcut';
1314

1415

1516
export function getHeader<T>(name: string, translate: boolean = true, addSorting: boolean = true): ColumnDefTemplate<HeaderContext<T, unknown>> | undefined {
@@ -68,6 +69,7 @@ interface ReactTableProps<T extends object> {
6869
maxHeight?: string | number;
6970
expandable?: boolean;
7071
storeSortingState?: string;
72+
rowId?: (originalRow: T, index: number, parent?: Row<T>) => string,
7173
}
7274

7375
function SimpleTable<T extends object>({
@@ -86,7 +88,8 @@ function SimpleTable<T extends object>({
8688
filters,
8789
maxHeight,
8890
expandable,
89-
storeSortingState
91+
storeSortingState,
92+
rowId
9093
}: ReactTableProps<T>) {
9194
const [pagination, setPagination] = useState<PaginationState>({
9295
pageIndex: pageIndex ?? 0,
@@ -97,6 +100,20 @@ function SimpleTable<T extends object>({
97100
const [expanded, setExpanded] = useState<ExpandedState>({});
98101
const [sorting, setSorting] = useState<SortingState>(getStoredSortingState(storeSortingState) || []);
99102

103+
useKeyboardShortcut(
104+
["Control", "A"],
105+
keys => {
106+
if (allowMultiSelect) {
107+
table.toggleAllRowsSelected(true);
108+
}
109+
},
110+
{
111+
overrideSystem: true,
112+
ignoreInputFields: true,
113+
repeatOnHold: false
114+
}
115+
);
116+
100117
const table = useReactTable({
101118
data,
102119
columns,
@@ -122,6 +139,7 @@ function SimpleTable<T extends object>({
122139
onExpandedChange: setExpanded,
123140
onSortingChange: setSorting,
124141
getSubRows: (row: any) => row?.subRows,
142+
getRowId: rowId,
125143
});
126144

127145
const { t } = useTranslation();

src/tribler/ui/src/config/menu.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ interface NavItem {
1616
}
1717

1818
interface NavItemWithChildren extends NavItem {
19-
items?: NavItemWithChildren[]
19+
items?: NavItem[]
2020
}
2121

2222
export const sideMenu: NavItemWithChildren[] = [

src/tribler/ui/src/pages/Downloads/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ export default function Downloads({ statusFilter }: { statusFilter: number[] })
195195
onSelectedRowsChange={setSelectedDownloads}
196196
maxHeight={Math.max((parentRect?.height ?? 50) - 50, 50)}
197197
storeSortingState="download-sorting"
198+
rowId={(row) => row.infohash}
198199
/>
199200
</Card>
200201
</div>

src/tribler/ui/src/pages/Popular/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export default function Popular() {
112112
data={torrents}
113113
columns={torrentColumns}
114114
storeSortingState="popular-sorting"
115+
rowId={(row) => row.infohash}
115116
/>
116117
</>
117118
)

src/tribler/ui/src/pages/Search/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export default function Search() {
151151
data={torrents}
152152
columns={torrentColumns}
153153
storeSortingState="search-sorting"
154+
rowId={(row) => row.infohash}
154155
/>
155156
</>
156157
)

0 commit comments

Comments
 (0)