- 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.
139 lines
3.8 KiB
TypeScript
139 lines
3.8 KiB
TypeScript
"use client"
|
|
|
|
import type { DriveFileInfo } from "@/lib/api/types"
|
|
import { displayFileName } from "@/lib/drive/display-file-name"
|
|
import {
|
|
driveFolderIconMeta,
|
|
driveMimeCategoryFor,
|
|
driveMimeIconMeta,
|
|
} from "@/lib/drive/drive-file-icon"
|
|
|
|
const PREVIEW_WIDTH_PX = 200
|
|
const PREVIEW_HEIGHT_PX = 48
|
|
|
|
const ICON_COLOR_HEX: Record<string, string> = {
|
|
"text-amber-500": "#f59e0b",
|
|
"text-amber-600": "#d97706",
|
|
"text-blue-500": "#3b82f6",
|
|
"text-blue-600": "#2563eb",
|
|
"text-green-600": "#16a34a",
|
|
"text-red-500": "#ef4444",
|
|
"text-red-600": "#dc2626",
|
|
"text-rose-500": "#f43f5e",
|
|
"text-[#5f6368]": "#5f6368",
|
|
}
|
|
|
|
let activePreview: HTMLElement | null = null
|
|
|
|
function iconMetaForFile(file: DriveFileInfo) {
|
|
const category = driveMimeCategoryFor(file)
|
|
if (category === "folder") return driveFolderIconMeta(file)
|
|
return driveMimeIconMeta(category)
|
|
}
|
|
|
|
function iconifySvgUrl(iconId: string, colorHex: string) {
|
|
const [prefix, name] = iconId.split(":")
|
|
return `https://api.iconify.design/${prefix}/${name}.svg?width=24&height=24&color=${encodeURIComponent(colorHex)}`
|
|
}
|
|
|
|
function appendTypeIcon(
|
|
root: HTMLElement,
|
|
file: DriveFileInfo,
|
|
dragSourceEl: HTMLElement | null
|
|
) {
|
|
const iconSlot = document.createElement("span")
|
|
iconSlot.setAttribute("data-drive-type-icon", "")
|
|
iconSlot.style.cssText =
|
|
"display:inline-flex;width:20px;height:20px;flex-shrink:0;align-items:center;justify-content:center;"
|
|
|
|
const cardRoot =
|
|
dragSourceEl?.closest("[data-drive-card]") ?? dragSourceEl ?? null
|
|
const liveSvg = cardRoot?.querySelector("[data-drive-type-icon] svg")
|
|
|
|
if (liveSvg) {
|
|
const svg = liveSvg.cloneNode(true) as SVGElement
|
|
svg.setAttribute("width", "20")
|
|
svg.setAttribute("height", "20")
|
|
svg.style.width = "20px"
|
|
svg.style.height = "20px"
|
|
iconSlot.appendChild(svg)
|
|
} else {
|
|
const { icon, color } = iconMetaForFile(file)
|
|
const img = document.createElement("img")
|
|
img.src = iconifySvgUrl(icon, ICON_COLOR_HEX[color] ?? "#5f6368")
|
|
img.width = 20
|
|
img.height = 20
|
|
img.alt = ""
|
|
img.draggable = false
|
|
img.style.display = "block"
|
|
iconSlot.appendChild(img)
|
|
}
|
|
|
|
root.appendChild(iconSlot)
|
|
}
|
|
|
|
function buildPreviewElement(
|
|
files: DriveFileInfo[],
|
|
dragSourceEl: HTMLElement | null
|
|
): HTMLElement {
|
|
const primary = files[0]
|
|
const label =
|
|
files.length === 1
|
|
? displayFileName(primary.name)
|
|
: `${files.length} élément${files.length > 1 ? "s" : ""}`
|
|
|
|
const root = document.createElement("div")
|
|
root.style.cssText = [
|
|
`width:${PREVIEW_WIDTH_PX}px`,
|
|
`height:${PREVIEW_HEIGHT_PX}px`,
|
|
"box-sizing:border-box",
|
|
"display:flex",
|
|
"align-items:center",
|
|
"gap:12px",
|
|
"padding:0 12px",
|
|
"border-radius:12px",
|
|
"border:1px solid rgba(232,234,237,0.8)",
|
|
"background:#f8f9fa",
|
|
"box-shadow:0 2px 8px rgba(60,64,67,0.28)",
|
|
"font-family:system-ui,sans-serif",
|
|
"font-size:14px",
|
|
"font-weight:500",
|
|
"color:#3c4043",
|
|
"pointer-events:none",
|
|
].join(";")
|
|
|
|
appendTypeIcon(root, primary, dragSourceEl)
|
|
|
|
const text = document.createElement("span")
|
|
text.textContent = label
|
|
text.style.cssText =
|
|
"min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;"
|
|
root.appendChild(text)
|
|
|
|
return root
|
|
}
|
|
|
|
export function applyDriveDragPreview(
|
|
dataTransfer: DataTransfer,
|
|
files: DriveFileInfo[],
|
|
dragSourceEl: HTMLElement | null
|
|
): void {
|
|
removeDriveDragPreview()
|
|
if (files.length === 0) return
|
|
|
|
const root = buildPreviewElement(files, dragSourceEl)
|
|
root.style.position = "fixed"
|
|
root.style.left = "-9999px"
|
|
root.style.top = "0"
|
|
root.style.zIndex = "99999"
|
|
document.body.appendChild(root)
|
|
activePreview = root
|
|
|
|
dataTransfer.setDragImage(root, PREVIEW_WIDTH_PX / 2, PREVIEW_HEIGHT_PX / 2)
|
|
}
|
|
|
|
export function removeDriveDragPreview(): void {
|
|
activePreview?.remove()
|
|
activePreview = null
|
|
}
|