68 lines
3.2 KiB
JavaScript
68 lines
3.2 KiB
JavaScript
import { useEffect, useRef } from 'react'
|
||
import { createPortal } from 'react-dom'
|
||
import { Eye, Download, Pencil, FolderInput, Trash2, RotateCcw, Settings2, Users, Share2 } from 'lucide-react'
|
||
|
||
export const fileOptions = (item, trash) => {
|
||
if (!item) return []
|
||
if (trash) return item.can_write ? [
|
||
{ id: 'restore', title: 'Restaurer', icon: RotateCcw },
|
||
{ id: 'confirmdelete', title: 'Supprimer définitivement', icon: Trash2, danger: true },
|
||
] : []
|
||
return [
|
||
{ id: 'open', title: 'Ouvrir', icon: Eye },
|
||
...(!item.is_folder ? [{ id: 'download', title: 'Télécharger', icon: Download }] : []),
|
||
...(item.can_write ? [{ id: 'share', title: 'Partager', icon: Share2 }] : []),
|
||
...(item.can_write ? [
|
||
{ id: 'rename', title: 'Renommer', icon: Pencil },
|
||
{ id: 'move', title: 'Déplacer', icon: FolderInput },
|
||
{ id: 'trash', title: 'Mettre à la corbeille', icon: Trash2, danger: true },
|
||
] : []),
|
||
]
|
||
}
|
||
|
||
export const spaceOptions = (space) => (space?.can_invite || space?.can_delete) ? [
|
||
...(space.can_edit ? [{id:'manage',title:'Paramètres de l’espace',icon:Settings2}] : []),
|
||
...(space.can_invite ? [{id:'members',title:'Accès et invitations',icon:Users}] : []),
|
||
...(space.can_delete ? [{id:'delete',title:space.parent_id ? 'Supprimer l’espace' : 'Vider la racine',icon:Trash2,danger:true}] : []),
|
||
] : []
|
||
|
||
/** Keyboard-accessible viewport-clamped menu used on desktop right-click and mobile ···. */
|
||
export default function ContextActions({ menu, onClose, onChoose }) {
|
||
const node = useRef(null)
|
||
useEffect(() => {
|
||
if (!menu) return
|
||
const onDown = event => { if (!node.current?.contains(event.target)) onClose() }
|
||
const onKey = event => { if (event.key === 'Escape') onClose() }
|
||
const onScroll = event => { if (!node.current?.contains(event.target)) onClose() }
|
||
document.addEventListener('pointerdown',onDown)
|
||
document.addEventListener('keydown',onKey)
|
||
window.addEventListener('scroll',onScroll,true)
|
||
node.current?.querySelector('button')?.focus()
|
||
return () => {
|
||
document.removeEventListener('pointerdown',onDown)
|
||
document.removeEventListener('keydown',onKey)
|
||
window.removeEventListener('scroll',onScroll,true)
|
||
}
|
||
}, [menu, onClose])
|
||
if (!menu) return null
|
||
const width=236
|
||
const height=menu.options.length*39+16
|
||
const x=Math.max(8,Math.min(menu.x,window.innerWidth-width-8))
|
||
const y=Math.max(8,Math.min(menu.y,window.innerHeight-height-8))
|
||
return createPortal(<div ref={node} role="menu" aria-label="Actions"
|
||
className="fixed z-[100] w-[236px] rounded-xl border bg-popover p-1.5 text-popover-foreground shadow-xl"
|
||
style={{left:x,top:y}}>
|
||
{menu.options.map(option => <button key={option.id} type="button" role="menuitem"
|
||
className={`flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm outline-none hover:bg-accent focus:bg-accent ${option.danger ? 'text-destructive' : ''}`}
|
||
onClick={() => { onClose(); onChoose(option.id, menu) }}>
|
||
<option.icon className="size-4 shrink-0" />{option.title}
|
||
</button>)}
|
||
</div>,document.body)
|
||
}
|
||
|
||
export function mobileDots(event, show, options) {
|
||
event.stopPropagation()
|
||
const rect=event.currentTarget.getBoundingClientRect()
|
||
show({ x:rect.right, y:rect.bottom+4, options })
|
||
}
|