685 lines
24 KiB
TypeScript
685 lines
24 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useRef, useEffect, type DragEvent } from "react"
|
|
import { MoreVertical } from "lucide-react"
|
|
import { cn } from "@/lib/utils"
|
|
import { useEmailDropTarget } from "@/lib/drag-context"
|
|
import {
|
|
MAIL_SIDEBAR_COLOR_PICKER_CLASS,
|
|
MAIL_SIDEBAR_MENU_PLAIN_ITEM_CLASS,
|
|
MAIL_SIDEBAR_MENU_SEPARATOR_CLASS,
|
|
MAIL_SIDEBAR_MENU_SUB_TRIGGER_CLASS,
|
|
MAIL_SIDEBAR_MENU_SURFACE_CLASS,
|
|
} from "@/lib/mail-chrome-classes"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuSub,
|
|
DropdownMenuSubContent,
|
|
DropdownMenuSubTrigger,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import {
|
|
ContextMenu,
|
|
ContextMenuContent,
|
|
ContextMenuItem,
|
|
ContextMenuLabel,
|
|
ContextMenuSeparator,
|
|
ContextMenuSub,
|
|
ContextMenuSubContent,
|
|
ContextMenuSubTrigger,
|
|
ContextMenuTrigger,
|
|
} from "@/components/ui/context-menu"
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog"
|
|
import { Input } from "@/components/ui/input"
|
|
import { Button } from "@/components/ui/button"
|
|
import { isSystemNavLabelId } from "@/lib/sidebar-nav-data"
|
|
import type {
|
|
LabelInMessageListVisibility,
|
|
LabelListSidebarVisibility,
|
|
NavItemPrefs,
|
|
} from "@/lib/sidebar-nav-context"
|
|
import {
|
|
readSidebarNavDragData,
|
|
resolveNavDropPlacement,
|
|
setSidebarNavDragData,
|
|
} from "@/lib/sidebar-nav-dnd"
|
|
import { LABEL_MENU_COLOR_SWATCHES } from "@/components/gmail/sidebar/sidebar-nav-constants"
|
|
import { NavColorPicker } from "@/components/gmail/nav/nav-color-picker"
|
|
import { normalizeNavColorClass } from "@/lib/nav-color"
|
|
import {
|
|
SidebarNavOptionsSheet,
|
|
SidebarNavSheetAction,
|
|
SidebarNavSheetCheckOption,
|
|
SidebarNavSheetColorPicker,
|
|
SidebarNavSheetDivider,
|
|
SidebarNavSheetSectionLabel,
|
|
} from "@/components/gmail/sidebar/sidebar-nav-options-sheet"
|
|
import { useSidebarTouchOptionsMenu } from "@/components/gmail/sidebar/use-sidebar-touch-options"
|
|
import {
|
|
LabelMenuOptionWithCheck,
|
|
ContextLabelMenuOptionWithCheck,
|
|
navRowRoundedWhenActive,
|
|
SidebarNavDragHandle,
|
|
SidebarNavIconSlot,
|
|
SidebarOverflowColumn,
|
|
sidebarOverflowMenuButtonClass,
|
|
navRowActivate,
|
|
} from "@/components/gmail/sidebar/sidebar-nav-primitives"
|
|
import type { SidebarNavDragBindings } from "@/components/gmail/sidebar/sidebar-nav-drag-bindings"
|
|
|
|
export type SidebarLabelItemRowProps = SidebarNavDragBindings & {
|
|
item: { id: string; label: string; color: string; count?: number }
|
|
unreadCount: number
|
|
isExpanded: boolean
|
|
selectedFolder: string
|
|
touchNav: boolean
|
|
onSelectFolder: (id: string) => void
|
|
getNavItemPrefs: (id: string) => Required<Pick<NavItemPrefs, "sidebar" | "messages">>
|
|
setNavItemSidebarVisibility: (id: string, v: LabelListSidebarVisibility) => void
|
|
setNavItemMessageVisibility: (id: string, v: LabelInMessageListVisibility) => void
|
|
updateFolderOrLabelColor: (id: string, color: string) => void
|
|
renameFolderOrLabel: (id: string, name: string) => void
|
|
removeFolderOrLabelRow: (id: string) => void
|
|
addChildLabelRow: (parentId: string, name: string) => void
|
|
}
|
|
|
|
export function SidebarLabelItemRow({
|
|
item,
|
|
unreadCount,
|
|
isExpanded: labelRowExpanded,
|
|
selectedFolder,
|
|
touchNav,
|
|
onSelectFolder,
|
|
getNavItemPrefs,
|
|
setNavItemSidebarVisibility,
|
|
setNavItemMessageVisibility,
|
|
updateFolderOrLabelColor,
|
|
renameFolderOrLabel,
|
|
removeFolderOrLabelRow,
|
|
addChildLabelRow,
|
|
navDragRef,
|
|
navDropPlacementRef,
|
|
beginNavDrag,
|
|
clearNavDrag,
|
|
updateNavDropTarget,
|
|
clearNavDropTarget,
|
|
commitNavDrop,
|
|
}: SidebarLabelItemRowProps) {
|
|
const { isOver, dropHandlers } = useEmailDropTarget(item.id, item.label)
|
|
const isSelected = selectedFolder === item.id
|
|
const hasUnread = unreadCount > 0
|
|
const [menuOpen, setMenuOpen] = useState(false)
|
|
const [contextMenuOpen, setContextMenuOpen] = useState(false)
|
|
const menuTriggerRef = useRef<HTMLButtonElement>(null)
|
|
const [renameOpen, setRenameOpen] = useState(false)
|
|
const [renameDraft, setRenameDraft] = useState(item.label)
|
|
const [sublabelOpen, setSublabelOpen] = useState(false)
|
|
const [sublabelName, setSublabelName] = useState("")
|
|
const labelRenameInputRef = useRef<HTMLInputElement>(null)
|
|
const sublabelNameInputRef = useRef<HTMLInputElement>(null)
|
|
const canDragLabel = labelRowExpanded && !isSystemNavLabelId(item.id)
|
|
const { sheetOpen, setSheetOpen, touchRowProps, touchRowClassName, closeSheet } =
|
|
useSidebarTouchOptionsMenu(touchNav && labelRowExpanded)
|
|
|
|
useEffect(() => {
|
|
setRenameDraft(item.label)
|
|
}, [item.label])
|
|
|
|
const handleMenuOpenChange = (open: boolean) => {
|
|
setMenuOpen(open)
|
|
if (!open) {
|
|
queueMicrotask(() => menuTriggerRef.current?.blur())
|
|
}
|
|
}
|
|
|
|
const rowHoverHeld =
|
|
!isSelected && !isOver && (contextMenuOpen || menuOpen || sheetOpen)
|
|
|
|
const prefs = getNavItemPrefs(item.id)
|
|
const labelDotClass = normalizeNavColorClass(item.color)
|
|
const labelMenuSurface =
|
|
MAIL_SIDEBAR_MENU_SURFACE_CLASS
|
|
|
|
const colorSub = (subKind: "dropdown" | "context") => {
|
|
const Sub = subKind === "dropdown" ? DropdownMenuSub : ContextMenuSub
|
|
const SubTr =
|
|
subKind === "dropdown" ? DropdownMenuSubTrigger : ContextMenuSubTrigger
|
|
const SubCo =
|
|
subKind === "dropdown" ? DropdownMenuSubContent : ContextMenuSubContent
|
|
return (
|
|
<Sub>
|
|
<SubTr
|
|
className={cn(
|
|
MAIL_SIDEBAR_MENU_SUB_TRIGGER_CLASS,
|
|
subKind === "context" && "flex items-center gap-2"
|
|
)}
|
|
>
|
|
<span className="flex size-5 shrink-0 items-center justify-center rounded-full border border-border bg-mail-surface">
|
|
<span
|
|
className={cn(
|
|
"block size-3 rounded-sm border border-black/10",
|
|
labelDotClass
|
|
)}
|
|
aria-hidden
|
|
/>
|
|
</span>
|
|
<span className="flex-1 text-left text-sm">Couleur du libellé</span>
|
|
</SubTr>
|
|
<SubCo className={MAIL_SIDEBAR_COLOR_PICKER_CLASS}>
|
|
<NavColorPicker
|
|
variant="menu"
|
|
value={labelDotClass}
|
|
swatches={LABEL_MENU_COLOR_SWATCHES}
|
|
onChange={(sw) => {
|
|
updateFolderOrLabelColor(item.id, sw)
|
|
setMenuOpen(false)
|
|
}}
|
|
/>
|
|
</SubCo>
|
|
</Sub>
|
|
)
|
|
}
|
|
|
|
const rowClass = cn(
|
|
"group/labelrow relative flex h-8 w-full min-w-0 shrink-0 cursor-default items-center pl-6 pr-2 transition-colors",
|
|
navRowRoundedWhenActive(isSelected || isOver || rowHoverHeld),
|
|
isSelected
|
|
? "bg-mail-nav-selected text-mail-nav-selected font-medium"
|
|
: isOver
|
|
? "bg-mail-nav-drop text-foreground"
|
|
: rowHoverHeld
|
|
? "bg-mail-nav-hover text-foreground"
|
|
: hasUnread
|
|
? "text-gray-900 hover:bg-mail-nav-hover"
|
|
: "text-gray-700 hover:bg-mail-nav-hover",
|
|
touchRowClassName
|
|
)
|
|
|
|
const onLabelRowDragEnter = (e: DragEvent) => {
|
|
const active = navDragRef.current
|
|
if (active?.kind === "label" && active.id !== item.id) {
|
|
e.preventDefault()
|
|
return
|
|
}
|
|
dropHandlers.onDragEnter(e)
|
|
}
|
|
|
|
const onLabelRowDragOver = (e: DragEvent) => {
|
|
const active = navDragRef.current
|
|
if (active?.kind === "label") {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
if (active.id === item.id) return
|
|
e.dataTransfer.dropEffect = "move"
|
|
updateNavDropTarget(
|
|
e.currentTarget as HTMLElement,
|
|
resolveNavDropPlacement(e, false)
|
|
)
|
|
return
|
|
}
|
|
dropHandlers.onDragOver(e)
|
|
}
|
|
|
|
const onLabelRowDragLeave = (e: DragEvent) => {
|
|
if (navDragRef.current?.kind === "label") {
|
|
const rt = e.relatedTarget as Node | null
|
|
if (rt && e.currentTarget instanceof Node && e.currentTarget.contains(rt)) return
|
|
clearNavDropTarget(e.currentTarget as HTMLElement)
|
|
return
|
|
}
|
|
dropHandlers.onDragLeave(e)
|
|
}
|
|
|
|
const onLabelRowDrop = (e: DragEvent) => {
|
|
const payload = readSidebarNavDragData(e, navDragRef.current)
|
|
if (payload?.kind === "label") {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
const placement = navDropPlacementRef.current ?? resolveNavDropPlacement(e, false)
|
|
if (placement !== "inside") {
|
|
commitNavDrop(payload, item.id, placement, "label")
|
|
} else {
|
|
clearNavDrag()
|
|
}
|
|
return
|
|
}
|
|
dropHandlers.onDrop(e)
|
|
}
|
|
|
|
const onLabelDragHandleStart = (e: DragEvent<HTMLSpanElement>) => {
|
|
const payload = { kind: "label" as const, id: item.id }
|
|
setSidebarNavDragData(e, payload)
|
|
const rowEl = (e.currentTarget as HTMLElement).closest("[data-nav-row]") as HTMLElement | null
|
|
beginNavDrag(payload, rowEl)
|
|
}
|
|
|
|
const overflowMenu = labelRowExpanded ? (
|
|
<SidebarOverflowColumn
|
|
unread={unreadCount}
|
|
menuOpen={menuOpen || sheetOpen}
|
|
hoverGroup="labelrow"
|
|
isSelected={isSelected}
|
|
hasUnread={hasUnread}
|
|
className="mr-[-7px]"
|
|
showMenuButton={!touchNav}
|
|
>
|
|
{!touchNav && (
|
|
<DropdownMenu open={menuOpen} onOpenChange={handleMenuOpenChange}>
|
|
<DropdownMenuTrigger asChild>
|
|
<button
|
|
ref={menuTriggerRef}
|
|
type="button"
|
|
draggable={false}
|
|
className={cn(sidebarOverflowMenuButtonClass, isSelected && "text-gray-900")}
|
|
aria-label={`Options pour ${item.label}`}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<MoreVertical className="h-4 w-4" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className={labelMenuSurface}>
|
|
{colorSub("dropdown")}
|
|
<DropdownMenuSeparator className={MAIL_SIDEBAR_MENU_SEPARATOR_CLASS} />
|
|
<DropdownMenuLabel className="px-3 py-1 text-[11px] font-normal normal-case tracking-normal text-muted-foreground">
|
|
Dans la liste des libellés
|
|
</DropdownMenuLabel>
|
|
<LabelMenuOptionWithCheck
|
|
checked={prefs.sidebar === "show"}
|
|
onPick={() => setNavItemSidebarVisibility(item.id, "show")}
|
|
>
|
|
Afficher
|
|
</LabelMenuOptionWithCheck>
|
|
<LabelMenuOptionWithCheck
|
|
checked={prefs.sidebar === "showUnread"}
|
|
onPick={() => setNavItemSidebarVisibility(item.id, "showUnread")}
|
|
>
|
|
Afficher si messages non lus
|
|
</LabelMenuOptionWithCheck>
|
|
<LabelMenuOptionWithCheck
|
|
checked={prefs.sidebar === "hide"}
|
|
onPick={() => setNavItemSidebarVisibility(item.id, "hide")}
|
|
>
|
|
Masquer
|
|
</LabelMenuOptionWithCheck>
|
|
<DropdownMenuSeparator className={MAIL_SIDEBAR_MENU_SEPARATOR_CLASS} />
|
|
<DropdownMenuLabel className="px-3 py-1 text-[11px] font-normal normal-case tracking-normal text-muted-foreground">
|
|
Dans la liste des messages
|
|
</DropdownMenuLabel>
|
|
<LabelMenuOptionWithCheck
|
|
checked={prefs.messages === "show"}
|
|
onPick={() => setNavItemMessageVisibility(item.id, "show")}
|
|
>
|
|
Afficher
|
|
</LabelMenuOptionWithCheck>
|
|
<LabelMenuOptionWithCheck
|
|
checked={prefs.messages === "hide"}
|
|
onPick={() => setNavItemMessageVisibility(item.id, "hide")}
|
|
>
|
|
Masquer
|
|
</LabelMenuOptionWithCheck>
|
|
<DropdownMenuSeparator className={MAIL_SIDEBAR_MENU_SEPARATOR_CLASS} />
|
|
<DropdownMenuItem
|
|
className={MAIL_SIDEBAR_MENU_PLAIN_ITEM_CLASS}
|
|
onClick={() => {
|
|
setRenameDraft(item.label)
|
|
setRenameOpen(true)
|
|
setMenuOpen(false)
|
|
}}
|
|
>
|
|
Renommer…
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
variant="destructive"
|
|
className="mx-1 cursor-pointer px-3 py-2 text-sm focus:bg-destructive/15"
|
|
onClick={() => {
|
|
removeFolderOrLabelRow(item.id)
|
|
setMenuOpen(false)
|
|
}}
|
|
>
|
|
Supprimer le libellé
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
className={MAIL_SIDEBAR_MENU_PLAIN_ITEM_CLASS}
|
|
onClick={() => {
|
|
setSublabelName("")
|
|
setSublabelOpen(true)
|
|
setMenuOpen(false)
|
|
}}
|
|
>
|
|
Ajouter un sous-libellé
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)}
|
|
</SidebarOverflowColumn>
|
|
) : null
|
|
|
|
const labelOptionsSheet = touchNav && labelRowExpanded && (
|
|
<SidebarNavOptionsSheet
|
|
open={sheetOpen}
|
|
onOpenChange={setSheetOpen}
|
|
title={item.label}
|
|
colorDotClass={labelDotClass}
|
|
>
|
|
<SidebarNavSheetColorPicker
|
|
title="Couleur du libellé"
|
|
dotClass={labelDotClass}
|
|
swatches={LABEL_MENU_COLOR_SWATCHES}
|
|
onPick={(sw) => {
|
|
updateFolderOrLabelColor(item.id, sw)
|
|
closeSheet()
|
|
}}
|
|
/>
|
|
<SidebarNavSheetDivider />
|
|
<SidebarNavSheetSectionLabel>Dans la liste des libellés</SidebarNavSheetSectionLabel>
|
|
<SidebarNavSheetCheckOption
|
|
checked={prefs.sidebar === "show"}
|
|
onPick={() => {
|
|
setNavItemSidebarVisibility(item.id, "show")
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Afficher
|
|
</SidebarNavSheetCheckOption>
|
|
<SidebarNavSheetCheckOption
|
|
checked={prefs.sidebar === "showUnread"}
|
|
onPick={() => {
|
|
setNavItemSidebarVisibility(item.id, "showUnread")
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Afficher si messages non lus
|
|
</SidebarNavSheetCheckOption>
|
|
<SidebarNavSheetCheckOption
|
|
checked={prefs.sidebar === "hide"}
|
|
onPick={() => {
|
|
setNavItemSidebarVisibility(item.id, "hide")
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Masquer
|
|
</SidebarNavSheetCheckOption>
|
|
<SidebarNavSheetDivider />
|
|
<SidebarNavSheetSectionLabel>Dans la liste des messages</SidebarNavSheetSectionLabel>
|
|
<SidebarNavSheetCheckOption
|
|
checked={prefs.messages === "show"}
|
|
onPick={() => {
|
|
setNavItemMessageVisibility(item.id, "show")
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Afficher
|
|
</SidebarNavSheetCheckOption>
|
|
<SidebarNavSheetCheckOption
|
|
checked={prefs.messages === "hide"}
|
|
onPick={() => {
|
|
setNavItemMessageVisibility(item.id, "hide")
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Masquer
|
|
</SidebarNavSheetCheckOption>
|
|
<SidebarNavSheetDivider />
|
|
<SidebarNavSheetAction
|
|
onClick={() => {
|
|
setRenameDraft(item.label)
|
|
setRenameOpen(true)
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Renommer…
|
|
</SidebarNavSheetAction>
|
|
<SidebarNavSheetAction
|
|
destructive
|
|
onClick={() => {
|
|
removeFolderOrLabelRow(item.id)
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Supprimer le libellé
|
|
</SidebarNavSheetAction>
|
|
<SidebarNavSheetAction
|
|
onClick={() => {
|
|
setSublabelName("")
|
|
setSublabelOpen(true)
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Ajouter un sous-libellé
|
|
</SidebarNavSheetAction>
|
|
</SidebarNavOptionsSheet>
|
|
)
|
|
|
|
const labelRowEl = (
|
|
<div
|
|
data-nav-row
|
|
{...touchRowProps}
|
|
onDragEnter={onLabelRowDragEnter}
|
|
onDragOver={onLabelRowDragOver}
|
|
onDragLeave={onLabelRowDragLeave}
|
|
onDrop={onLabelRowDrop}
|
|
className={rowClass}
|
|
>
|
|
{canDragLabel ? (
|
|
<SidebarNavDragHandle
|
|
label={item.label}
|
|
onDragStart={onLabelDragHandleStart}
|
|
onDragEnd={clearNavDrag}
|
|
/>
|
|
) : null}
|
|
<div
|
|
role="button"
|
|
tabIndex={0}
|
|
title={!labelRowExpanded ? item.label : undefined}
|
|
onClick={() => onSelectFolder(item.id)}
|
|
onKeyDown={(e) => navRowActivate(e, () => onSelectFolder(item.id))}
|
|
className={cn(
|
|
"flex h-8 min-w-0 flex-1 cursor-pointer items-center gap-4 py-0 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring/50",
|
|
labelRowExpanded ? "pr-1" : "pr-3"
|
|
)}
|
|
>
|
|
<SidebarNavIconSlot showUnreadDot={hasUnread}>
|
|
<span
|
|
className={cn("block h-3 w-3 rounded-sm", item.color ?? "bg-gray-400")}
|
|
/>
|
|
</SidebarNavIconSlot>
|
|
{labelRowExpanded && (
|
|
<span
|
|
className={cn(
|
|
"min-w-0 flex-1 truncate text-sm leading-5",
|
|
hasUnread && !isSelected && "font-semibold text-gray-900"
|
|
)}
|
|
>
|
|
{item.label}
|
|
</span>
|
|
)}
|
|
</div>
|
|
{overflowMenu}
|
|
</div>
|
|
)
|
|
|
|
return (
|
|
<>
|
|
{touchNav ? (
|
|
labelRowEl
|
|
) : (
|
|
<ContextMenu onOpenChange={setContextMenuOpen}>
|
|
<ContextMenuTrigger asChild>{labelRowEl}</ContextMenuTrigger>
|
|
<ContextMenuContent className={labelMenuSurface}>
|
|
{colorSub("context")}
|
|
<ContextMenuSeparator className={MAIL_SIDEBAR_MENU_SEPARATOR_CLASS} />
|
|
<ContextMenuLabel className="px-3 py-1 text-[11px] font-normal normal-case tracking-normal text-muted-foreground">
|
|
Dans la liste des libellés
|
|
</ContextMenuLabel>
|
|
<ContextLabelMenuOptionWithCheck
|
|
checked={prefs.sidebar === "show"}
|
|
onPick={() => setNavItemSidebarVisibility(item.id, "show")}
|
|
>
|
|
Afficher
|
|
</ContextLabelMenuOptionWithCheck>
|
|
<ContextLabelMenuOptionWithCheck
|
|
checked={prefs.sidebar === "showUnread"}
|
|
onPick={() => setNavItemSidebarVisibility(item.id, "showUnread")}
|
|
>
|
|
Afficher si non lus
|
|
</ContextLabelMenuOptionWithCheck>
|
|
<ContextLabelMenuOptionWithCheck
|
|
checked={prefs.sidebar === "hide"}
|
|
onPick={() => setNavItemSidebarVisibility(item.id, "hide")}
|
|
>
|
|
Masquer
|
|
</ContextLabelMenuOptionWithCheck>
|
|
<ContextMenuSeparator className={MAIL_SIDEBAR_MENU_SEPARATOR_CLASS} />
|
|
<ContextMenuLabel className="px-3 py-1 text-[11px] font-normal normal-case tracking-normal text-muted-foreground">
|
|
Dans la liste des messages
|
|
</ContextMenuLabel>
|
|
<ContextLabelMenuOptionWithCheck
|
|
checked={prefs.messages === "show"}
|
|
onPick={() => setNavItemMessageVisibility(item.id, "show")}
|
|
>
|
|
Afficher
|
|
</ContextLabelMenuOptionWithCheck>
|
|
<ContextLabelMenuOptionWithCheck
|
|
checked={prefs.messages === "hide"}
|
|
onPick={() => setNavItemMessageVisibility(item.id, "hide")}
|
|
>
|
|
Masquer
|
|
</ContextLabelMenuOptionWithCheck>
|
|
<ContextMenuSeparator className="my-1.5 bg-gray-200" />
|
|
<ContextMenuItem
|
|
className="mx-1 cursor-pointer px-3 py-2 text-sm"
|
|
onClick={() => {
|
|
setRenameDraft(item.label)
|
|
setRenameOpen(true)
|
|
}}
|
|
>
|
|
Renommer…
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
variant="destructive"
|
|
className="mx-1 cursor-pointer px-3 py-2 text-sm"
|
|
onClick={() => removeFolderOrLabelRow(item.id)}
|
|
>
|
|
Supprimer le libellé
|
|
</ContextMenuItem>
|
|
<ContextMenuItem
|
|
className="mx-1 cursor-pointer px-3 py-2 text-sm"
|
|
onClick={() => {
|
|
setSublabelName("")
|
|
setSublabelOpen(true)
|
|
}}
|
|
>
|
|
Ajouter un sous-libellé
|
|
</ContextMenuItem>
|
|
</ContextMenuContent>
|
|
</ContextMenu>
|
|
)}
|
|
{labelOptionsSheet}
|
|
|
|
<Dialog open={renameOpen} onOpenChange={setRenameOpen}>
|
|
<DialogContent
|
|
className="sm:max-w-md"
|
|
showCloseButton
|
|
onOpenAutoFocus={(e) => {
|
|
e.preventDefault()
|
|
window.requestAnimationFrame(() =>
|
|
labelRenameInputRef.current?.focus()
|
|
)
|
|
}}
|
|
>
|
|
<DialogHeader>
|
|
<DialogTitle>Renommer le libellé</DialogTitle>
|
|
<DialogDescription>Nouveau nom pour « {item.label} ».</DialogDescription>
|
|
</DialogHeader>
|
|
<Input
|
|
ref={labelRenameInputRef}
|
|
value={renameDraft}
|
|
onChange={(e) => setRenameDraft(e.target.value)}
|
|
autoComplete="off"
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault()
|
|
renameFolderOrLabel(item.id, renameDraft)
|
|
setRenameOpen(false)
|
|
}
|
|
}}
|
|
/>
|
|
<DialogFooter>
|
|
<Button variant="outline" type="button" onClick={() => setRenameOpen(false)}>
|
|
Annuler
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
onClick={() => {
|
|
renameFolderOrLabel(item.id, renameDraft)
|
|
setRenameOpen(false)
|
|
}}
|
|
>
|
|
Enregistrer
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
<Dialog open={sublabelOpen} onOpenChange={setSublabelOpen}>
|
|
<DialogContent
|
|
className="sm:max-w-md"
|
|
showCloseButton
|
|
onOpenAutoFocus={(e) => {
|
|
e.preventDefault()
|
|
window.requestAnimationFrame(() =>
|
|
sublabelNameInputRef.current?.focus()
|
|
)
|
|
}}
|
|
>
|
|
<DialogHeader>
|
|
<DialogTitle>Sous-libellé</DialogTitle>
|
|
<DialogDescription>
|
|
Sera créé sous « {item.label} » (chemin type Parent/Enfant).
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<Input
|
|
ref={sublabelNameInputRef}
|
|
value={sublabelName}
|
|
onChange={(e) => setSublabelName(e.target.value)}
|
|
placeholder="Nom du sous-libellé"
|
|
autoComplete="off"
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault()
|
|
addChildLabelRow(item.id, sublabelName)
|
|
setSublabelOpen(false)
|
|
}
|
|
}}
|
|
/>
|
|
<DialogFooter>
|
|
<Button variant="outline" type="button" onClick={() => setSublabelOpen(false)}>
|
|
Annuler
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
onClick={() => {
|
|
addChildLabelRow(item.id, sublabelName)
|
|
setSublabelOpen(false)
|
|
}}
|
|
>
|
|
Créer
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
)
|
|
}
|