66import * as React from 'react' ;
77import { PullRequest } from '../../src/github/views' ;
88
9+ import PullRequestContext from '../common/context' ;
910import { AddComment , CommentView } from '../components/comment' ;
10- import { Header } from '../components/header' ;
11+ import { getStatus , Header } from '../components/header' ;
12+ import { copyIcon } from '../components/icon' ;
1113import { StatusChecksSection } from '../components/merge' ;
1214import Sidebar , { CollapsibleSidebar } from '../components/sidebar' ;
1315import { Timeline } from '../components/timeline' ;
@@ -29,11 +31,35 @@ const useMediaQuery = (query: string) => {
2931 return matches ;
3032} ;
3133
34+ function useStickyHeader ( titleRef : React . RefObject < HTMLDivElement | null > ) : boolean {
35+ const [ isStuck , setIsStuck ] = React . useState ( false ) ;
36+
37+ React . useEffect ( ( ) => {
38+ const el = titleRef . current ;
39+ if ( ! el ) {
40+ return ;
41+ }
42+
43+ const observer = new IntersectionObserver (
44+ ( [ entry ] ) => setIsStuck ( ! entry . isIntersecting ) ,
45+ { threshold : 0 } ,
46+ ) ;
47+ observer . observe ( el ) ;
48+
49+ return ( ) => observer . disconnect ( ) ;
50+ } , [ titleRef ] ) ;
51+
52+ return isStuck ;
53+ }
54+
3255export const Overview = ( pr : PullRequest ) => {
3356 const isSingleColumnLayout = useMediaQuery ( '(max-width: 768px)' ) ;
57+ const titleRef = React . useRef < HTMLDivElement > ( null ) ;
58+ const isStuck = useStickyHeader ( titleRef ) ;
3459
3560 return < >
36- < div id = "title" className = "title" >
61+ < StickyHeader pr = { pr } visible = { isStuck } />
62+ < div id = "title" className = "title" ref = { titleRef } >
3763 < div className = "details" >
3864 < Header { ...pr } />
3965 </ div >
@@ -52,6 +78,27 @@ export const Overview = (pr: PullRequest) => {
5278 </ > ;
5379} ;
5480
81+ function StickyHeader ( { pr, visible } : { pr : PullRequest ; visible : boolean } ) : JSX . Element {
82+ const { text, color, icon } = getStatus ( pr . state , ! ! pr . isDraft , pr . isIssue , pr . stateReason ) ;
83+ const { copyPrLink } = React . useContext ( PullRequestContext ) ;
84+
85+ return (
86+ < div className = { `sticky-header${ visible ? ' visible' : '' } ` } >
87+ < div className = "sticky-header-left" >
88+ < div id = "sticky-status" className = { `status-badge-${ color } ` } >
89+ < span className = "icon" > { icon } </ span >
90+ < span > { text } </ span >
91+ </ div >
92+ < span className = "sticky-header-title" dangerouslySetInnerHTML = { { __html : pr . titleHTML } } />
93+ < a className = "sticky-header-number" href = { pr . url } > #{ pr . number } </ a >
94+ < button title = "Copy Link" onClick = { copyPrLink } className = "icon-button sticky-header-copy" aria-label = "Copy Pull Request Link" >
95+ { copyIcon }
96+ </ button >
97+ </ div >
98+ </ div >
99+ ) ;
100+ }
101+
55102const Main = ( pr : PullRequest ) => (
56103 < div id = "main" >
57104 < div id = "description" >
0 commit comments