ultisuite-client/components/suite/suite-favorites-menu.tsx
R3D347HR4Y 6ec95262af Add OnlyOffice integration and update project configurations
- Updated .env.example to include configuration for OnlyOffice Document Server.
- Modified the workspace configuration to remove the drive-suite path.
- Adjusted TypeScript environment imports for consistency.
- Enhanced Next.js configuration to disable canvas in Webpack.
- Updated package.json to include new dependencies for OnlyOffice and PDF.js.
- Added global styles for OnlyOffice theme integration in the CSS.
- Created new layout and page components for the Drive feature, including public sharing and editing functionalities.
- Updated metadata handling across various layouts to reflect the new app structure.
2026-06-07 15:49:21 +02:00

164 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useEffect, useRef, useState } from "react"
import Link from "next/link"
import { LayoutGrid, Pencil } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
SUITE_FAVORITE_APPS,
type FavoriteApp,
} from "@/lib/suite/favorite-apps"
import { SUITE_HEADER_DROPDOWN_CLASS, SUITE_ICON_BTN } from "@/lib/suite/suite-chrome-classes"
import { cn } from "@/lib/utils"
const FAVORITE_TILE_CLASS =
"flex flex-col items-center gap-2 rounded-lg p-3 transition-colors hover:bg-accent"
const DEFAULT_ICON_BTN_CLASS = cn(
"rounded-full",
SUITE_ICON_BTN,
"hover:text-accent-foreground",
)
function FavoriteAppTile({
app,
onNavigate,
}: {
app: FavoriteApp
onNavigate?: () => void
}) {
const content = (
<>
<div className="flex h-10 w-10 items-center justify-center">
<img
src={app.icon}
alt=""
className={cn(
"h-10 w-10 object-contain",
app.whiteLogoInDark && "dark:invert dark:hue-rotate-180",
)}
onError={(e) => {
const target = e.target as HTMLImageElement
target.style.display = "none"
target.parentElement!.innerHTML = `<div class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-500 font-bold text-white">${app.name[0]}</div>`
}}
/>
</div>
<span className="w-full text-center text-xs text-muted-foreground">
{app.name}
</span>
</>
)
if (!app.href) {
return (
<button type="button" className={FAVORITE_TILE_CLASS} disabled>
{content}
</button>
)
}
if (app.external) {
return (
<a
href={app.href}
target="_blank"
rel="noopener noreferrer"
className={FAVORITE_TILE_CLASS}
onClick={onNavigate}
>
{content}
</a>
)
}
return (
<Link href={app.href} className={FAVORITE_TILE_CLASS} onClick={onNavigate}>
{content}
</Link>
)
}
interface SuiteFavoritesMenuProps {
className?: string
iconButtonClass?: string
dropdownClass?: string
/** Fermer un autre panneau header (ex. compte) à louverture. */
onOpen?: () => void
}
export function SuiteFavoritesMenu({
className,
iconButtonClass = DEFAULT_ICON_BTN_CLASS,
dropdownClass = SUITE_HEADER_DROPDOWN_CLASS,
onOpen,
}: SuiteFavoritesMenuProps) {
const [open, setOpen] = useState(false)
const menuRef = useRef<HTMLDivElement>(null)
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setOpen(false)
}
}
document.addEventListener("mousedown", handleClickOutside)
return () => document.removeEventListener("mousedown", handleClickOutside)
}, [])
return (
<div className={cn("relative hidden sm:block", className)} ref={menuRef}>
<Button
variant="ghost"
size="icon"
className={iconButtonClass}
aria-label="Applications"
aria-expanded={open}
aria-haspopup="dialog"
onClick={() => {
const next = !open
setOpen(next)
if (next) onOpen?.()
}}
>
<LayoutGrid className="size-6 shrink-0" aria-hidden />
</Button>
{open ? (
<div
className={cn(
"absolute right-0 top-12 z-50 w-96 rounded-2xl",
dropdownClass,
)}
role="dialog"
aria-label="Vos favoris"
>
<div className="flex items-center justify-between border-b border-border p-4">
<span className="text-lg font-normal text-foreground">
Vos favoris
</span>
<Button
variant="ghost"
size="icon"
className={cn("h-8 w-8", iconButtonClass)}
aria-label="Personnaliser les favoris"
disabled
>
<Pencil className="h-4 w-4" />
</Button>
</div>
<div className="grid grid-cols-3 gap-1 p-3">
{SUITE_FAVORITE_APPS.map((app) => (
<FavoriteAppTile
key={app.name}
app={app}
onNavigate={() => setOpen(false)}
/>
))}
</div>
</div>
) : null}
</div>
)
}