- 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.
106 lines
3.0 KiB
TypeScript
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>
|
|
)
|
|
}
|