"use client"; import { useEffect, useRef, useState } from "react"; import Link from "@/components/routing/app-link"; import { Button } from "@kandev/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger, } from "@kandev/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip"; import { IconPlugConnected } from "@tabler/icons-react"; import { DestinationRows } from "@/components/navigation/destination-rows"; import { useAppDestinations } from "@/hooks/use-app-destinations"; import { useTranslation } from "react-i18next"; type MobileIntegrationsSectionProps = { onNavigate: () => void; }; const HOVER_CLOSE_DELAY_MS = 180; /** * Configured integration destinations, in manifest order. Ids, labels, hrefs and * icons come from `lib/navigation/core-destinations.ts`; availability comes from * `useNavAvailability` through `useAppDestinations`. */ function useIntegrationDestinations() { return useAppDestinations("sidebar", "integrations"); } export function IntegrationsMenu() { const { t } = useTranslation(); const links = useIntegrationDestinations(); const [open, setOpen] = useState(false); const closeTimeoutRef = useRef | null>(null); useEffect(() => { return () => { if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); }; }, []); const clearCloseTimeout = () => { if (!closeTimeoutRef.current) return; clearTimeout(closeTimeoutRef.current); closeTimeoutRef.current = null; }; const openOnHover = () => { clearCloseTimeout(); setOpen((current) => (current ? current : true)); }; const closeAfterHover = () => { clearCloseTimeout(); closeTimeoutRef.current = setTimeout(() => setOpen(false), HOVER_CLOSE_DELAY_MS); }; const handleOpenChange = (nextOpen: boolean) => { clearCloseTimeout(); setOpen(nextOpen); }; if (links.length === 0) return null; return ( {t("common:integrations")} {links.map((link) => { const Icon = link.icon; return ( {link.label} ); })} ); } export function IntegrationsTopbarLinks() { const links = useIntegrationDestinations(); if (links.length === 0) return null; return ( <> {links.map((link) => { const Icon = link.icon; return ( {link.label} ); })} ); } /** * Mobile counterpart to the desktop sidebar Integrations section: the * hamburger-sheet surface that exposes the first-party integration links plus * any plugin-registered nav items targeting this section * (`registerNavItem({ section: "integrations" })`), matching the desktop * `IntegrationsSection`. Both come from the navigation manifest, so the two * surfaces cannot drift apart. */ export function MobileIntegrationsSection({ onNavigate }: MobileIntegrationsSectionProps) { const { t } = useTranslation(); const destinations = useAppDestinations("mobileMenu", "integrations"); if (destinations.length === 0) return null; return (
{t("common:integrations")}
); }