import { ChevronRight, FileText, Folder, FolderOpen } from "lucide-react"; import React, { useEffect, useRef, useState } from "react"; import { cn } from "../../lib/utils"; type VaultTreeInlineRenameInputProps = { initialName: string; onCommit: (name: string) => boolean | void | Promise; onCancel: () => void; className?: string; style?: React.CSSProperties; }; export const VaultTreeInlineRenameInput: React.FC = ({ initialName, onCommit, onCancel, className, style, }) => { const inputRef = useRef(null); const [value, setValue] = useState(initialName); const submittingRef = useRef(false); useEffect(() => { const input = inputRef.current; if (!input) return; input.focus(); input.select(); }, []); const commit = async () => { if (submittingRef.current) return; submittingRef.current = true; try { const committed = await onCommit(value); if (committed === false) submittingRef.current = false; } catch { submittingRef.current = false; } }; const cancel = () => { if (submittingRef.current) return; submittingRef.current = true; onCancel(); }; return ( setValue(event.target.value)} onBlur={() => { queueMicrotask(() => { void commit(); }); }} onClick={(event) => event.stopPropagation()} onDoubleClick={(event) => event.stopPropagation()} onMouseDown={(event) => event.stopPropagation()} onPointerDown={(event) => event.stopPropagation()} onDragStart={(event) => { event.preventDefault(); event.stopPropagation(); }} onKeyDown={(event) => { event.stopPropagation(); if (event.key === "Enter") { event.preventDefault(); void commit(); } if (event.key === "Escape") { event.preventDefault(); cancel(); } }} className={cn( "min-w-0 flex-1 truncate select-text rounded-sm border border-primary/50 bg-background/80 px-1 py-0 text-sm font-medium outline-none ring-1 ring-primary/30", className, )} style={style} /> ); }; type VaultTreeGroupRowProps = Omit, "children"> & { name: string; depth: number; expanded?: boolean; selected?: boolean; count?: number; hasChildren?: boolean; editing?: boolean; editingInitialName?: string; onRenameCommit?: (name: string) => boolean | void | Promise; onRenameCancel?: () => void; actions?: React.ReactNode; labelActions?: React.ReactNode; icon?: React.ReactNode; iconSize?: number; iconClassName?: string; meta?: React.ReactNode; rowRef?: React.Ref; onToggle?: () => void; }; export const VaultTreeGroupRow: React.FC = ({ name, depth, expanded = false, selected = false, count, hasChildren, editing = false, editingInitialName, onRenameCommit, onRenameCancel, actions, labelActions, icon, iconSize = 18, iconClassName, meta, rowRef, className, style, ...props }) => { const canExpand = hasChildren ?? Boolean(count); return (
{canExpand && (
)}
{icon ?? (expanded ? ( ) : ( ))}
{editing && onRenameCommit && onRenameCancel ? ( ) : ( // leading-5 (not leading-none): CJK fallbacks like PingFang paint outside a 1.0 em box and get clipped by truncate. {name} {labelActions ? {labelActions} : null} )} {meta ?
{meta}
: null} {typeof count === "number" && count > 0 && ( {count} )} {actions ?
{actions}
: null}
); }; type VaultTreeItemRowProps = Omit, "children"> & { label: string; depth: number; selected?: boolean; icon?: React.ReactNode; iconClassName?: string; leading?: React.ReactNode; detail?: React.ReactNode; actions?: React.ReactNode; editing?: boolean; editingInitialName?: string; onRenameCommit?: (name: string) => boolean | void | Promise; onRenameCancel?: () => void; content?: React.ReactNode; }; export const VaultTreeItemRow: React.FC = ({ label, depth, selected = false, icon, iconClassName, leading, detail, actions, editing = false, editingInitialName, onRenameCommit, onRenameCancel, content, className, style, ...props }) => (
{leading ??
}
{icon ?? }
{content ?? (
{editing && onRenameCommit && onRenameCancel ? ( ) : ( // leading-5 keeps CJK glyphs inside the line box under truncate overflow.
{label}
)} {detail &&
{detail}
}
)} {actions ?
{actions}
: null}
);