fix(ui): keep timeline preview tooltip interactive
Allow pointer interaction with the message preview tooltip and delay hover dismissal so users can move from the timeline segment onto the preview to copy or delete.
This commit is contained in:
@@ -276,6 +276,7 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
|||||||
const [tooltipSize, setTooltipSize] = createSignal<{ width: number; height: number }>({ width: 360, height: 420 })
|
const [tooltipSize, setTooltipSize] = createSignal<{ width: number; height: number }>({ width: 360, height: 420 })
|
||||||
const [tooltipElement, setTooltipElement] = createSignal<HTMLDivElement | null>(null)
|
const [tooltipElement, setTooltipElement] = createSignal<HTMLDivElement | null>(null)
|
||||||
let hoverTimer: number | null = null
|
let hoverTimer: number | null = null
|
||||||
|
let closeTimer: number | null = null
|
||||||
const showTools = () => props.showToolSegments ?? true
|
const showTools = () => props.showToolSegments ?? true
|
||||||
|
|
||||||
const registerButtonRef = (segmentId: string, element: HTMLButtonElement | null) => {
|
const registerButtonRef = (segmentId: string, element: HTMLButtonElement | null) => {
|
||||||
@@ -293,9 +294,29 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearCloseTimer = () => {
|
||||||
|
if (closeTimer !== null && typeof window !== "undefined") {
|
||||||
|
window.clearTimeout(closeTimer)
|
||||||
|
closeTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const scheduleClose = () => {
|
||||||
|
if (typeof window === "undefined") return
|
||||||
|
clearHoverTimer()
|
||||||
|
clearCloseTimer()
|
||||||
|
// Small delay so the pointer can travel from the segment to the tooltip.
|
||||||
|
closeTimer = window.setTimeout(() => {
|
||||||
|
closeTimer = null
|
||||||
|
setHoveredSegment(null)
|
||||||
|
setHoverAnchorRect(null)
|
||||||
|
}, 160)
|
||||||
|
}
|
||||||
|
|
||||||
const handleMouseEnter = (segment: TimelineSegment, event: MouseEvent) => {
|
const handleMouseEnter = (segment: TimelineSegment, event: MouseEvent) => {
|
||||||
if (typeof window === "undefined") return
|
if (typeof window === "undefined") return
|
||||||
clearHoverTimer()
|
clearHoverTimer()
|
||||||
|
clearCloseTimer()
|
||||||
const target = event.currentTarget as HTMLButtonElement
|
const target = event.currentTarget as HTMLButtonElement
|
||||||
hoverTimer = window.setTimeout(() => {
|
hoverTimer = window.setTimeout(() => {
|
||||||
const rect = target.getBoundingClientRect()
|
const rect = target.getBoundingClientRect()
|
||||||
@@ -305,9 +326,7 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
clearHoverTimer()
|
scheduleClose()
|
||||||
setHoveredSegment(null)
|
|
||||||
setHoverAnchorRect(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
@@ -326,7 +345,10 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
|||||||
setTooltipCoords({ top: clampedTop, left: clampedLeft })
|
setTooltipCoords({ top: clampedTop, left: clampedLeft })
|
||||||
})
|
})
|
||||||
|
|
||||||
onCleanup(() => clearHoverTimer())
|
onCleanup(() => {
|
||||||
|
clearHoverTimer()
|
||||||
|
clearCloseTimer()
|
||||||
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const activeId = props.activeMessageId
|
const activeId = props.activeMessageId
|
||||||
@@ -432,6 +454,8 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
|||||||
ref={(element) => setTooltipElement(element)}
|
ref={(element) => setTooltipElement(element)}
|
||||||
class="message-timeline-tooltip"
|
class="message-timeline-tooltip"
|
||||||
style={{ top: `${tooltipCoords().top}px`, left: `${tooltipCoords().left}px` }}
|
style={{ top: `${tooltipCoords().top}px`, left: `${tooltipCoords().left}px` }}
|
||||||
|
onMouseEnter={() => clearCloseTimer()}
|
||||||
|
onMouseLeave={() => scheduleClose()}
|
||||||
>
|
>
|
||||||
<MessagePreview
|
<MessagePreview
|
||||||
messageId={data().messageId}
|
messageId={data().messageId}
|
||||||
|
|||||||
@@ -211,7 +211,8 @@
|
|||||||
.message-timeline-tooltip {
|
.message-timeline-tooltip {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
pointer-events: none;
|
/* Allow interacting with the preview (copy/delete/etc). */
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-preview {
|
.message-preview {
|
||||||
|
|||||||
Reference in New Issue
Block a user