diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index a0699d2..9560f27 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -32,8 +32,8 @@ import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, - useSidebar, } from '@/components/ui/sidebar'; +import { useSidebar } from '@/components/ui/sidebar-context'; import { cn } from '@/lib/utils'; export interface SidebarNavItem { diff --git a/src/components/common/LobeProviderIcon.tsx b/src/components/common/LobeProviderIcon.tsx index 8b3359d..21d7117 100644 --- a/src/components/common/LobeProviderIcon.tsx +++ b/src/components/common/LobeProviderIcon.tsx @@ -61,7 +61,7 @@ const PROVIDER_ICON_MAP: Record = { 'x-ai': XAIIcon, }; -export interface LobeProviderIconProps { +interface LobeProviderIconProps { className?: string; fallbackLabel?: string; provider?: string | null; @@ -70,16 +70,12 @@ export interface LobeProviderIconProps { title?: string; } -export const normalizeLobeProviderKey = (value?: string | null) => +const normalizeLobeProviderKey = (value?: string | null) => String(value ?? '') .trim() .toLowerCase() .replace(/[\s_]+/g, '-'); -export function hasLobeProviderIcon(provider?: string | null): boolean { - return Boolean(PROVIDER_ICON_MAP[normalizeLobeProviderKey(provider)]); -} - export function LobeProviderIcon({ className, fallbackLabel, diff --git a/src/components/config/VisualConfigEditor.tsx b/src/components/config/VisualConfigEditor.tsx index 853d177..8ec287a 100644 --- a/src/components/config/VisualConfigEditor.tsx +++ b/src/components/config/VisualConfigEditor.tsx @@ -18,7 +18,7 @@ import { TimerIcon, } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; -import { buttonVariants } from '@/components/ui/button'; +import { buttonVariants } from '@/components/ui/button-variants'; import { Field, FieldDescription, diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index cacff11..2eb5513 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -46,4 +46,4 @@ function Badge({ ) } -export { Badge, badgeVariants } +export { Badge } diff --git a/src/components/ui/button-group.tsx b/src/components/ui/button-group.tsx index 692fb07..ebab590 100644 --- a/src/components/ui/button-group.tsx +++ b/src/components/ui/button-group.tsx @@ -79,5 +79,4 @@ export { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, - buttonGroupVariants, } diff --git a/src/components/ui/button-variants.ts b/src/components/ui/button-variants.ts new file mode 100644 index 0000000..186216b --- /dev/null +++ b/src/components/ui/button-variants.ts @@ -0,0 +1,46 @@ +import { cva, type VariantProps } from "class-variance-authority" + +const buttonVariants = cva( + "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/80", + primary: "bg-primary text-primary-foreground hover:bg-primary/80", + outline: + "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", + ghost: + "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", + destructive: + "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", + danger: + "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: + "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + md: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", + lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + icon: "size-8", + "icon-xs": + "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", + "icon-sm": + "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg", + "icon-lg": "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +type ButtonVariantProps = VariantProps + +export { buttonVariants, type ButtonVariantProps } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 6d46597..ebe072d 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,51 +1,10 @@ import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" import { LoaderCircleIcon } from "lucide-react" import { Slot } from "radix-ui" +import { buttonVariants, type ButtonVariantProps } from "@/components/ui/button-variants" import { cn } from "@/lib/utils" -const buttonVariants = cva( - "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/80", - primary: "bg-primary text-primary-foreground hover:bg-primary/80", - outline: - "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", - secondary: - "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", - ghost: - "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", - destructive: - "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", - danger: - "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: - "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - md: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", - sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", - lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - icon: "size-8", - "icon-xs": - "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", - "icon-sm": - "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg", - "icon-lg": "size-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) - function Button({ children, className, @@ -57,12 +16,24 @@ function Button({ disabled, ...props }: React.ComponentProps<"button"> & - VariantProps & { + ButtonVariantProps & { asChild?: boolean fullWidth?: boolean loading?: boolean }) { const Comp = asChild ? Slot.Root : "button" + const loader = loading ? ( + + ) : null + const slottedChildren = + asChild && loader && React.isValidElement(children) + ? React.cloneElement( + children, + undefined, + loader, + (children.props as { children?: React.ReactNode }).children + ) + : children return ( - {loading && } - {children} + {asChild ? ( + slottedChildren + ) : ( + <> + {loader} + {children} + + )} ) } -export { Button, buttonVariants } +export { Button } diff --git a/src/components/ui/sidebar-context.ts b/src/components/ui/sidebar-context.ts new file mode 100644 index 0000000..825851b --- /dev/null +++ b/src/components/ui/sidebar-context.ts @@ -0,0 +1,24 @@ +import * as React from "react" + +type SidebarContextProps = { + state: "expanded" | "collapsed" + open: boolean + setOpen: (open: boolean) => void + openMobile: boolean + setOpenMobile: (open: boolean) => void + isMobile: boolean + toggleSidebar: () => void +} + +const SidebarContext = React.createContext(null) + +function useSidebar() { + const context = React.useContext(SidebarContext) + if (!context) { + throw new Error("useSidebar must be used within a SidebarProvider.") + } + + return context +} + +export { SidebarContext, useSidebar, type SidebarContextProps } diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 1389fc5..9585f70 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -7,6 +7,11 @@ import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Separator } from "@/components/ui/separator" +import { + SidebarContext, + useSidebar, + type SidebarContextProps, +} from "@/components/ui/sidebar-context" import { Sheet, SheetContent, @@ -29,27 +34,6 @@ const SIDEBAR_WIDTH_MOBILE = "18rem" const SIDEBAR_WIDTH_ICON = "3rem" const SIDEBAR_KEYBOARD_SHORTCUT = "b" -type SidebarContextProps = { - state: "expanded" | "collapsed" - open: boolean - setOpen: (open: boolean) => void - openMobile: boolean - setOpenMobile: (open: boolean) => void - isMobile: boolean - toggleSidebar: () => void -} - -const SidebarContext = React.createContext(null) - -function useSidebar() { - const context = React.useContext(SidebarContext) - if (!context) { - throw new Error("useSidebar must be used within a SidebarProvider.") - } - - return context -} - function SidebarProvider({ defaultOpen = true, open: openProp, @@ -696,5 +680,4 @@ export { SidebarRail, SidebarSeparator, SidebarTrigger, - useSidebar, } diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index 72465b2..432125c 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -85,4 +85,4 @@ function TabsContent({ ) } -export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/src/components/ui/toggle-group.tsx b/src/components/ui/toggle-group.tsx index f797b05..ec01c8f 100644 --- a/src/components/ui/toggle-group.tsx +++ b/src/components/ui/toggle-group.tsx @@ -1,14 +1,13 @@ "use client" import * as React from "react" -import { type VariantProps } from "class-variance-authority" import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui" import { cn } from "@/lib/utils" -import { toggleVariants } from "@/components/ui/toggle" +import { toggleVariants, type ToggleVariantProps } from "@/components/ui/toggle-variants" const ToggleGroupContext = React.createContext< - VariantProps & { + ToggleVariantProps & { spacing?: number orientation?: "horizontal" | "vertical" } @@ -28,7 +27,7 @@ function ToggleGroup({ children, ...props }: React.ComponentProps & - VariantProps & { + ToggleVariantProps & { spacing?: number orientation?: "horizontal" | "vertical" }) { @@ -62,7 +61,7 @@ function ToggleGroupItem({ size = "default", ...props }: React.ComponentProps & - VariantProps) { + ToggleVariantProps) { const context = React.useContext(ToggleGroupContext) return ( diff --git a/src/components/ui/toggle-variants.ts b/src/components/ui/toggle-variants.ts new file mode 100644 index 0000000..c4283f8 --- /dev/null +++ b/src/components/ui/toggle-variants.ts @@ -0,0 +1,27 @@ +import { cva, type VariantProps } from "class-variance-authority" + +const toggleVariants = cva( + "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-transparent", + outline: "border border-input bg-transparent hover:bg-muted", + }, + size: { + default: + "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", + lg: "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +type ToggleVariantProps = VariantProps + +export { toggleVariants, type ToggleVariantProps } diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx index 8791a0a..1db5316 100644 --- a/src/components/ui/toggle.tsx +++ b/src/components/ui/toggle.tsx @@ -1,30 +1,8 @@ import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" import { Toggle as TogglePrimitive } from "radix-ui" import { cn } from "@/lib/utils" - -const toggleVariants = cva( - "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", - { - variants: { - variant: { - default: "bg-transparent", - outline: "border border-input bg-transparent hover:bg-muted", - }, - size: { - default: - "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", - lg: "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) +import { toggleVariants, type ToggleVariantProps } from "@/components/ui/toggle-variants" function Toggle({ className, @@ -32,7 +10,7 @@ function Toggle({ size = "default", ...props }: React.ComponentProps & - VariantProps) { + ToggleVariantProps) { return ( ({ apiKey: '', proxyUrl: '', }); +const AUTO_TEST_MODEL_VALUE = '__auto__'; const stripDisableAllRule = (list?: string[]): string[] => (list ?? []).filter((s) => s.trim() !== '*'); @@ -309,7 +310,9 @@ export function BaseProviderForm({ const autoLabel = firstName ? t('providersPage.form.testModelAutoWith', { name: firstName }) : t('providersPage.form.testModelAutoEmpty'); - const opts: Array<{ value: string; label: string }> = [{ value: '', label: autoLabel }]; + const opts: Array<{ value: string; label: string }> = [ + { value: AUTO_TEST_MODEL_VALUE, label: autoLabel }, + ]; names.forEach((n) => opts.push({ value: n, label: n })); const tm = (form.testModel ?? '').trim(); if (tm && !seen.has(tm)) { @@ -321,6 +324,8 @@ export function BaseProviderForm({ return opts; }, [form.models, form.testModel, t]); + const testModelValue = form.testModel?.trim() ? form.testModel : AUTO_TEST_MODEL_VALUE; + const openDiscovery = () => { setDiscoveryOpen(true); if (!discovery.loading && !discovery.hasFetched) { @@ -606,9 +611,11 @@ export function BaseProviderForm({ updateField('testModel', value)} + onChange={(value) => + updateField('testModel', value === AUTO_TEST_MODEL_VALUE ? '' : value) + } disabled={mutating} ariaLabel={t('providersPage.form.testModel')} /> diff --git a/src/pages/AuthFilesPage.tsx b/src/pages/AuthFilesPage.tsx index 5848798..cefcdea 100644 --- a/src/pages/AuthFilesPage.tsx +++ b/src/pages/AuthFilesPage.tsx @@ -112,13 +112,23 @@ export function AuthFilesPage() { const pageTabParam = searchParams.get('tab'); const activePageTab: AuthFilesPageTab = isAuthFilesPageTab(pageTabParam) ? pageTabParam : 'files'; - const [filter, setFilter] = useState<'all' | string>('all'); - const [page, setPage] = useState(1); + const [initialUiState] = useState(() => readAuthFilesUiState()); + const [filter, setFilter] = useState<'all' | string>(() => { + const persistedFilter = initialUiState?.filter; + return typeof persistedFilter === 'string' && persistedFilter.trim() + ? normalizeProviderKey(persistedFilter) + : 'all'; + }); + const [page, setPage] = useState(() => { + const persistedPage = initialUiState?.page; + return typeof persistedPage === 'number' && Number.isFinite(persistedPage) + ? Math.max(1, Math.round(persistedPage)) + : 1; + }); const [viewMode, setViewMode] = useState<'diagram' | 'list'>('list'); const [accountDialogOpen, setAccountDialogOpen] = useState(false); const [accountChannel, setAccountChannel] = useState('codex'); const [batchActionBarVisible, setBatchActionBarVisible] = useState(false); - const [uiStateHydrated, setUiStateHydrated] = useState(false); const floatingBatchActionsRef = useRef(null); const batchActionAnimationRef = useRef(null); const previousSelectionCountRef = useRef(0); @@ -199,33 +209,12 @@ export function AuthFilesPage() { const pageSize = DEFAULT_REGULAR_PAGE_SIZE; useEffect(() => { - const persisted = readAuthFilesUiState(); - if (persisted) { - if (typeof persisted.filter === 'string' && persisted.filter.trim()) { - setFilter(normalizeProviderKey(persisted.filter)); - } - if (typeof persisted.page === 'number' && Number.isFinite(persisted.page)) { - setPage(Math.max(1, Math.round(persisted.page))); - } - } - - setUiStateHydrated(true); - }, []); - - useEffect(() => { - if (!uiStateHydrated) return; - writeAuthFilesUiState({ filter, page, pageSize, }); - }, [ - filter, - page, - pageSize, - uiStateHydrated, - ]); + }, [filter, page, pageSize]); const handleHeaderRefresh = useCallback(async () => { await Promise.all([loadFiles(), loadExcluded(), loadModelAlias()]); @@ -346,6 +335,59 @@ export function AuthFilesPage() { batchStatusUpdating || selectedHasStatusUpdating; + const showBatchActionBar = useCallback(() => { + setBatchActionBarVisible(true); + }, []); + + const handleToggleAllPageItems = useCallback(() => { + if (allPageItemsSelected) { + invertVisibleSelection(selectablePageItems); + return; + } + showBatchActionBar(); + selectAllVisible(pageItems); + }, [ + allPageItemsSelected, + invertVisibleSelection, + pageItems, + selectablePageItems, + selectAllVisible, + showBatchActionBar, + ]); + + const handleToggleFileSelection = useCallback( + (name: string) => { + if (!selectedFiles.has(name)) { + showBatchActionBar(); + } + toggleSelect(name); + }, + [selectedFiles, showBatchActionBar, toggleSelect] + ); + + const handleSelectPageItems = useCallback(() => { + showBatchActionBar(); + selectAllVisible(pageItems); + }, [pageItems, selectAllVisible, showBatchActionBar]); + + const handleSelectFilteredItems = useCallback(() => { + showBatchActionBar(); + selectAllVisible(sorted); + }, [selectAllVisible, showBatchActionBar, sorted]); + + const handleInvertPageSelection = useCallback(() => { + if (selectablePageItems.some((file) => !selectedFiles.has(file.name))) { + showBatchActionBar(); + } + invertVisibleSelection(pageItems); + }, [ + invertVisibleSelection, + pageItems, + selectablePageItems, + selectedFiles, + showBatchActionBar, + ]); + const copyTextWithNotification = useCallback( async (text: string) => { const copied = await copyToClipboard(text); @@ -418,9 +460,6 @@ export function AuthFilesPage() { useEffect(() => { selectionCountRef.current = selectionCount; - if (selectionCount > 0) { - setBatchActionBarVisible(true); - } }, [selectionCount]); useLayoutEffect(() => { @@ -520,13 +559,7 @@ export function AuthFilesPage() { ? t('auth_files.batch_deselect') : t('auth_files.batch_select_page') } - onCheckedChange={() => { - if (allPageItemsSelected) { - invertVisibleSelection(selectablePageItems); - return; - } - selectAllVisible(pageItems); - }} + onCheckedChange={handleToggleAllPageItems} /> @@ -563,7 +596,7 @@ export function AuthFilesPage() { ? t('auth_files.batch_deselect') : t('auth_files.batch_select_all') } - onCheckedChange={() => toggleSelect(file.name)} + onCheckedChange={() => handleToggleFileSelection(file.name)} /> )} @@ -925,7 +958,7 @@ export function AuthFilesPage() {