diff --git a/desktop/src/features/messages/ui/MessageDepthGuides.tsx b/desktop/src/features/messages/ui/MessageDepthGuides.tsx new file mode 100644 index 0000000000..976ab09ddf --- /dev/null +++ b/desktop/src/features/messages/ui/MessageDepthGuides.tsx @@ -0,0 +1,106 @@ +import * as React from "react"; + +import { + threadReplyLength, + THREAD_REPLY_LINE_WIDTH_REM, +} from "@/features/messages/lib/threadTreeLayout"; +import type { TimelineMessage } from "@/features/messages/types"; +import { cn } from "@/shared/lib/cn"; + +type DepthGuideAction = { + active?: boolean; + depth: number; + label: string; + message: TimelineMessage; +}; + +export function MessageDepthGuides({ + collapseDepthGuideActionsByDepth, + depthGuideItems, + guideBleedRem, + highlightThreadLineDepths, + messageId, + onCollapse, + onHoverChange, +}: { + collapseDepthGuideActionsByDepth: ReadonlyMap; + depthGuideItems: ReadonlyArray<{ depth: number; offset: number }>; + guideBleedRem: number; + highlightThreadLineDepths?: ReadonlyArray; + messageId: string; + onCollapse: ( + event: React.MouseEvent, + message: TimelineMessage, + ) => void; + onHoverChange: (message: TimelineMessage, active: boolean) => void; +}) { + return ( +
0 ? undefined : true} + className={cn( + "absolute left-0", + collapseDepthGuideActionsByDepth.size === 0 && "pointer-events-none", + )} + style={{ + bottom: threadReplyLength(-guideBleedRem), + top: threadReplyLength(-guideBleedRem), + }} + > + {depthGuideItems.map(({ depth, offset }) => { + const collapseAction = collapseDepthGuideActionsByDepth.get(depth); + const isHighlighted = + Boolean(collapseAction?.active) || + Boolean(highlightThreadLineDepths?.includes(depth)); + if (collapseAction) { + return ( + +
+