From 29a57fcbe619d8d7c722523b938ab98759dd2581 Mon Sep 17 00:00:00 2001 From: Maciej Bula Date: Sun, 26 Apr 2026 21:52:43 +0200 Subject: [PATCH 1/2] fix: columns dropdown on safari --- src/components/FilterBar.tsx | 49 +++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/components/FilterBar.tsx b/src/components/FilterBar.tsx index aee0076..9d39940 100644 --- a/src/components/FilterBar.tsx +++ b/src/components/FilterBar.tsx @@ -1,4 +1,5 @@ import { useState, useRef, useCallback, type FC } from 'react' +import { createPortal } from 'react-dom' import { LayoutGrid, Download, @@ -333,9 +334,18 @@ interface ColumnSelectorProps { export function ColumnSelector({ columns, visible, onChange, columnOrder, onReorder, onReset }: ColumnSelectorProps) { const [open, setOpen] = useState(false) const [draggedId, setDraggedId] = useState(null) - const ref = useRef(null) - const close = useCallback(() => setOpen(false), []) - useClickOutside(ref, close) + const [pos, setPos] = useState<{ top: number; right: number } | null>(null) + const buttonRef = useRef(null) + + function handleToggle() { + if (open) { + setOpen(false) + return + } + const rect = buttonRef.current?.getBoundingClientRect() + if (rect) setPos({ top: rect.bottom, right: window.innerWidth - rect.right }) + setOpen(true) + } function toggle(id: string) { const next = new Set(visible) @@ -370,9 +380,10 @@ export function ColumnSelector({ columns, visible, onChange, columnOrder, onReor .filter((c): c is ColumnDef => c !== undefined) return ( -
+ <> - {open && ( -
+ {open && pos && createPortal( + <> +
setOpen(false)} + style={{ position: 'fixed', inset: 0, zIndex: 99 }} + /> +
))} -
+
+ , + document.body )} -
+ ) } From 793f007216f6c04b12af3ffee170cba85b4e41a3 Mon Sep 17 00:00:00 2001 From: Maciej Bula Date: Sun, 26 Apr 2026 21:52:56 +0200 Subject: [PATCH 2/2] fix: case-only filename clash in contexts --- bun.lock | 32 +++++++++---------- src/App.tsx | 4 +-- ...stanceContext.tsx => InstanceProvider.tsx} | 0 ...tionContext.tsx => PaginationProvider.tsx} | 0 src/mobile/MobileApp.tsx | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) rename src/contexts/{InstanceContext.tsx => InstanceProvider.tsx} (100%) rename src/contexts/{PaginationContext.tsx => PaginationProvider.tsx} (100%) diff --git a/bun.lock b/bun.lock index 5d75236..cca5e40 100644 --- a/bun.lock +++ b/bun.lock @@ -5,46 +5,46 @@ "": { "name": "qbitwebui", "dependencies": { - "@tanstack/react-query": "^5.90.21", + "@tanstack/react-query": "^5.99.0", "colord": "^2.9.3", - "hono": "^4.11.9", + "hono": "^4.12.14", "jszip": "^3.10.1", "lucide-react": "^0.563.0", - "react": "^19.2.4", + "react": "^19.2.5", "react-colorful": "^5.6.1", - "react-dom": "^19.2.4", - "tar-stream": "^3.1.7", + "react-dom": "^19.2.5", + "tar-stream": "^3.1.8", "vaul": "^1.1.2", "xml2js": "^0.6.2", }, "devDependencies": { - "@eslint/js": "^9.39.2", - "@tailwindcss/vite": "^4.1.18", + "@eslint/js": "^9.39.4", + "@tailwindcss/vite": "^4.2.2", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@types/bun": "^1.3.9", + "@types/bun": "^1.3.12", "@types/jsdom": "^27.0.0", - "@types/node": "^24.10.13", + "@types/node": "^24.12.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/tar-stream": "^3.1.4", "@types/xml2js": "^0.4.14", - "@vitejs/plugin-react": "^5.1.4", + "@vitejs/plugin-react": "^5.2.0", "@vitest/coverage-v8": "4.0.17", "concurrently": "^9.2.1", - "eslint": "^9.39.2", + "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.26", "globals": "^16.5.0", "jsdom": "^27.4.0", "picocolors": "^1.1.1", - "prettier": "^3.8.1", - "tailwindcss": "^4.1.18", + "prettier": "^3.8.3", + "tailwindcss": "^4.2.2", "typescript": "~5.9.3", - "typescript-eslint": "^8.55.0", - "vite": "^7.3.1", + "typescript-eslint": "^8.58.2", + "vite": "^7.3.2", "vitepress": "^1.6.4", - "vitest": "^4.0.18", + "vitest": "^4.1.4", }, }, }, diff --git a/src/App.tsx b/src/App.tsx index bb46eb5..e252ac5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,8 @@ import { useState, useEffect, useCallback, lazy, Suspense } from 'react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ThemeProvider } from './contexts/ThemeProvider' -import { InstanceProvider } from './contexts/InstanceContext' -import { PaginationProvider } from './contexts/PaginationContext' +import { InstanceProvider } from './contexts/InstanceProvider' +import { PaginationProvider } from './contexts/PaginationProvider' import { Layout } from './components/Layout' import { AuthForm } from './components/AuthForm' import { InstanceManager } from './components/InstanceManager' diff --git a/src/contexts/InstanceContext.tsx b/src/contexts/InstanceProvider.tsx similarity index 100% rename from src/contexts/InstanceContext.tsx rename to src/contexts/InstanceProvider.tsx diff --git a/src/contexts/PaginationContext.tsx b/src/contexts/PaginationProvider.tsx similarity index 100% rename from src/contexts/PaginationContext.tsx rename to src/contexts/PaginationProvider.tsx diff --git a/src/mobile/MobileApp.tsx b/src/mobile/MobileApp.tsx index 9270dcf..d03a656 100644 --- a/src/mobile/MobileApp.tsx +++ b/src/mobile/MobileApp.tsx @@ -8,7 +8,7 @@ import { MobileInstancePicker } from './MobileInstancePicker' import { MobileStats } from './MobileStats' import { MobileTorrentList } from './MobileTorrentList' import { MobileThemeSwitcher } from './MobileThemeSwitcher' -import { InstanceProvider } from '../contexts/InstanceContext' +import { InstanceProvider } from '../contexts/InstanceProvider' const MobileTorrentDetail = lazy(() => import('./MobileTorrentDetail').then((m) => ({ default: m.MobileTorrentDetail }))