ultisuite-client/components/gmail/email-list/attachments/email-list-attachment-row.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

106 lines
3.0 KiB
TypeScript

"use client"
import {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from "react"
import type { EmailAttachment } from "@/lib/email-data"
import { ListAttachmentChip } from "./list-attachment-chip"
export function EmailListAttachmentRow({
emailId,
attachments,
}: {
emailId: string
attachments: EmailAttachment[]
}) {
const containerRef = useRef<HTMLDivElement>(null)
const measureRef = useRef<HTMLDivElement>(null)
const [collapsed, setCollapsed] = useState(false)
const attachSig = attachments.map((a) => `${a.name}\u0001${a.kind ?? ""}`).join("\u0002")
const updateCollapsed = useCallback(() => {
const container = containerRef.current
const measure = measureRef.current
if (!container || !measure || attachments.length <= 1) {
setCollapsed(false)
return
}
const available = container.clientWidth
const needed = measure.scrollWidth
setCollapsed(needed > available + 1)
}, [attachSig, attachments.length])
useLayoutEffect(() => {
updateCollapsed()
}, [updateCollapsed])
useEffect(() => {
const el = containerRef.current
if (!el || typeof ResizeObserver === "undefined") return
const ro = new ResizeObserver(() => updateCollapsed())
ro.observe(el)
return () => ro.disconnect()
}, [updateCollapsed])
const othersLabel =
attachments.length - 1 === 1 ? "1 autre" : `${attachments.length - 1} autres`
const othersTitle = attachments
.slice(1)
.map((a) => a.name)
.join(", ")
return (
<div ref={containerRef} className="relative min-w-0 w-full">
{attachments.length > 1 && (
<div
ref={measureRef}
className="pointer-events-none invisible absolute left-0 top-0 z-[-1] flex w-max flex-nowrap gap-1.5"
aria-hidden
>
{attachments.map((att, idx) => (
<ListAttachmentChip
key={`${emailId}-m-${idx}`}
att={att}
messageId={emailId}
attachments={attachments}
attachmentIndex={idx}
/>
))}
</div>
)}
<div className="flex min-w-0 flex-nowrap items-center gap-1.5 overflow-hidden">
{collapsed && attachments.length > 1 ? (
<>
<ListAttachmentChip
att={attachments[0]!}
messageId={emailId}
attachments={attachments}
attachmentIndex={0}
/>
<span
className="shrink-0 rounded-full border border-mail-list-chip-border bg-mail-list-chip-muted px-2.5 py-1 text-[13px] leading-snug text-muted-foreground"
title={othersTitle}
>
{othersLabel}
</span>
</>
) : (
attachments.map((att, idx) => (
<ListAttachmentChip
key={`${emailId}-v-${idx}`}
att={att}
messageId={emailId}
attachments={attachments}
attachmentIndex={idx}
/>
))
)}
</div>
</div>
)
}