дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
Replaces the Pull Requests tool window with a full review workflow on top of gh's authentication. - List with search, quick filters and paging; a tab per pull request and a New Pull Request tab. - Details: commit picker, changes tree with viewed state, CI and reviewer status, merge, close and reopen. - Timeline editor: comments, review threads, reactions and suggested changes. - Review in the diff viewer and, for the checked-out branch, in the editor (review mode). - New generic vcs extension points: registerDiffViewerProvider, lineInlays, reviewGutter, branch widget extensions; new vcs_merge_base and gh_api commands. - CSP: img-src allows https://avatars.githubusercontent.com for avatars. Fixed while testing against a real repository: remotes behind an SSH host alias, mergeability stuck on UNKNOWN, Apply Locally staying disabled after a checkout, relative links navigating the app's page, and review inlays wider than the pane. Co-authored-by: imacross <[email protected]>
78 рядки
2.2 KiB
TypeScript
78 рядки
2.2 KiB
TypeScript
/**
|
|
* The row's hover balloons (`ReviewListCellRenderer.LazyIdeToolTip`): after the tooltip
|
|
* delay, a balloon to the right of the labels icon or an avatar stack, titled with the
|
|
* collection's name ("Labels", "Reviewers") over a list of icon + text rows. It stays
|
|
* open while the pointer is over the trigger or the balloon.
|
|
*/
|
|
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
import { Popup, TOOLTIP_DELAY } from "@/ui";
|
|
import styles from "./List.module.css";
|
|
|
|
export interface BalloonItem {
|
|
key: string;
|
|
icon: ReactNode;
|
|
text: string;
|
|
}
|
|
|
|
const HIDE_GRACE_MS = 120;
|
|
|
|
export function HoverBalloon({
|
|
title,
|
|
items,
|
|
children,
|
|
className,
|
|
}: {
|
|
title: string;
|
|
items: readonly BalloonItem[];
|
|
children: ReactNode;
|
|
className?: string;
|
|
}) {
|
|
const anchor = useRef<HTMLSpanElement>(null);
|
|
const [open, setOpen] = useState(false);
|
|
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
|
useEffect(() => () => clearTimeout(timer.current), []);
|
|
const later = (next: boolean, ms: number) => {
|
|
clearTimeout(timer.current);
|
|
timer.current = setTimeout(() => setOpen(next), ms);
|
|
};
|
|
const enter = () => (open ? clearTimeout(timer.current) : later(true, TOOLTIP_DELAY));
|
|
const leave = () => (open ? later(false, HIDE_GRACE_MS) : clearTimeout(timer.current));
|
|
return (
|
|
<>
|
|
<span
|
|
ref={anchor}
|
|
className={className}
|
|
onPointerEnter={enter}
|
|
onPointerLeave={leave}
|
|
onPointerDown={() => {
|
|
clearTimeout(timer.current);
|
|
setOpen(false);
|
|
}}
|
|
>
|
|
{children}
|
|
</span>
|
|
<Popup
|
|
open={open}
|
|
anchor={anchor}
|
|
placement="right"
|
|
offset={6}
|
|
role="tooltip"
|
|
autoFocus={false}
|
|
restoreFocus={false}
|
|
onClose={() => setOpen(false)}
|
|
className={styles.balloon}
|
|
>
|
|
<div onPointerEnter={enter} onPointerLeave={leave}>
|
|
<div className={styles.balloonTitle}>{title}</div>
|
|
{items.map((item) => (
|
|
<div key={item.key} className={styles.balloonRow}>
|
|
{item.icon}
|
|
<span>{item.text}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Popup>
|
|
</>
|
|
);
|
|
}
|