diff --git a/gradle/changelog/sticky_diff.yaml b/gradle/changelog/sticky_diff.yaml new file mode 100644 index 0000000000..5ce0f1b960 --- /dev/null +++ b/gradle/changelog/sticky_diff.yaml @@ -0,0 +1,2 @@ +- type: changed + description: Make diff header sticky diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 0e11dcff36..a85e6380fe 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -22635,7 +22635,7 @@ exports[`Storyshots Repositories/Diff Binaries 1`] = ` id="main-java" >
= ({ modalTitle, modalBody, tooltipStyle return <>{content}; } return ( - + {content} ); diff --git a/scm-ui/ui-components/src/repos/DiffButton.tsx b/scm-ui/ui-components/src/repos/DiffButton.tsx index 397389bfb4..2da1ad6d0e 100644 --- a/scm-ui/ui-components/src/repos/DiffButton.tsx +++ b/scm-ui/ui-components/src/repos/DiffButton.tsx @@ -45,7 +45,7 @@ const DiffButton: FC = ({ icon, tooltip, onClick }) => { }; return ( - + diff --git a/scm-ui/ui-components/src/repos/DiffTypes.ts b/scm-ui/ui-components/src/repos/DiffTypes.ts index 2562513b0f..64aba86ce0 100644 --- a/scm-ui/ui-components/src/repos/DiffTypes.ts +++ b/scm-ui/ui-components/src/repos/DiffTypes.ts @@ -64,6 +64,7 @@ export type DiffObjectProps = { isCollapsed?: (file: File) => boolean; onCollapseStateChange?: (file: File, newState?: boolean) => void; hunkClass?: (hunk: Hunk) => string; + stickyHeader?: boolean; /** * Fontawesome Icon Unicode * diff --git a/scm-ui/ui-components/src/repos/JumpToFileButton.tsx b/scm-ui/ui-components/src/repos/JumpToFileButton.tsx index b77e579506..703674b637 100644 --- a/scm-ui/ui-components/src/repos/JumpToFileButton.tsx +++ b/scm-ui/ui-components/src/repos/JumpToFileButton.tsx @@ -41,7 +41,7 @@ type Props = { const JumpToFileButton: FC = ({ link, tooltip }) => { return ( - + diff --git a/scm-ui/ui-components/src/repos/LazyDiffFile.tsx b/scm-ui/ui-components/src/repos/LazyDiffFile.tsx index f8d64f4892..5b79682edc 100644 --- a/scm-ui/ui-components/src/repos/LazyDiffFile.tsx +++ b/scm-ui/ui-components/src/repos/LazyDiffFile.tsx @@ -61,8 +61,10 @@ type State = Collapsible & { expansionError?: any; }; -const StyledHunk = styled(Hunk)`${props => { - let style = props.icon ? ` +const StyledHunk = styled(Hunk)` + ${(props) => { + let style = props.icon + ? ` .diff-gutter:hover::after { font-size: inherit; margin-left: 0.5em; @@ -70,23 +72,25 @@ const StyledHunk = styled(Hunk)`${props => { content: "${props.icon}"; color: var(--scm-column-selection); } - ` : ""; - if (!props.actionable) { - style += ` + ` + : ""; + if (!props.actionable) { + style += ` .diff-gutter { cursor: default; } `; - } - if (props.highlightLineOnHover) { - style += ` + } + if (props.highlightLineOnHover) { + style += ` tr.diff-line:hover > td { background-color: var(--sh-selected-color); } `; - } - return style; -}}`; + } + return style; + }} +`; const DiffFilePanel = styled.div` /* remove bottom border for collapsed panels */ @@ -106,10 +110,20 @@ const MarginlessModalContent = styled.div` } `; +const PanelHeading = styled.div<{ sticky: boolean }>` + ${(props) => + props.sticky + ? ` + position: sticky; + top: 52px; + ` + : ""} +`; + class DiffFile extends React.Component { static defaultProps: Partial = { defaultCollapse: false, - markConflicts: true + markConflicts: true, }; constructor(props: Props) { @@ -118,14 +132,14 @@ class DiffFile extends React.Component { collapsed: this.defaultCollapse(), sideBySide: props.sideBySide, diffExpander: new DiffExpander(props.file), - file: props.file + file: props.file, }; } componentDidUpdate(prevProps: Readonly) { if (!this.props.isCollapsed && this.props.defaultCollapse !== prevProps.defaultCollapse) { this.setState({ - collapsed: this.defaultCollapse() + collapsed: this.defaultCollapse(), }); } } @@ -148,8 +162,8 @@ class DiffFile extends React.Component { if (onCollapseStateChange) { onCollapseStateChange(file); } else { - this.setState(state => ({ - collapsed: !state.collapsed + this.setState((state) => ({ + collapsed: !state.collapsed, })); } } @@ -157,8 +171,8 @@ class DiffFile extends React.Component { toggleSideBySide = (callback: () => void) => { this.setState( - state => ({ - sideBySide: !state.sideBySide + (state) => ({ + sideBySide: !state.sideBySide, }), () => callback() ); @@ -170,7 +184,7 @@ class DiffFile extends React.Component { onCollapseStateChange(this.state.file, collapsed); } else { this.setState({ - collapsed + collapsed, }); } }; @@ -264,19 +278,13 @@ class DiffFile extends React.Component { expandHead = (expandableHunk: ExpandableHunk, count: number) => { return () => { - return expandableHunk - .expandHead(count) - .then(this.diffExpanded) - .catch(this.diffExpansionFailed); + return expandableHunk.expandHead(count).then(this.diffExpanded).catch(this.diffExpansionFailed); }; }; expandBottom = (expandableHunk: ExpandableHunk, count: number) => { return () => { - return expandableHunk - .expandBottom(count) - .then(this.diffExpanded) - .catch(this.diffExpansionFailed); + return expandableHunk.expandBottom(count).then(this.diffExpanded).catch(this.diffExpansionFailed); }; }; @@ -294,7 +302,7 @@ class DiffFile extends React.Component { if (annotationFactory) { return annotationFactory({ hunk, - file + file, }); } else { return EMPTY_ANNOTATION_FACTORY; @@ -308,7 +316,7 @@ class DiffFile extends React.Component { changeId: getChangeKey(change), change, hunk, - file + file, }; if (onClick) { onClick(context); @@ -321,7 +329,7 @@ class DiffFile extends React.Component { return { onClick: (event: ChangeEvent) => { this.handleClickEvent(event.change, hunk); - } + }, }; } }; @@ -448,7 +456,7 @@ class DiffFile extends React.Component { hasContent = (file: FileDiff) => file && !file.isBinary && file.hunks && file.hunks.length > 0; render() { - const { fileControlFactory, fileAnnotationFactory, t } = this.props; + const { fileControlFactory, fileAnnotationFactory, stickyHeader = false, t } = this.props; const { file, sideBySide, diffExpander, expansionError } = this.state; const viewType = sideBySide ? "split" : "unified"; const collapsed = this.isCollapsed(); @@ -527,7 +535,7 @@ class DiffFile extends React.Component { id={this.getAnchorId(file)} > {errorModal} -
+
{ {headerButtons}
-
+ {body} ); diff --git a/scm-ui/ui-components/src/repos/changesets/ChangesetDiff.tsx b/scm-ui/ui-components/src/repos/changesets/ChangesetDiff.tsx index fe1e646ebb..5b152baaea 100644 --- a/scm-ui/ui-components/src/repos/changesets/ChangesetDiff.tsx +++ b/scm-ui/ui-components/src/repos/changesets/ChangesetDiff.tsx @@ -60,6 +60,7 @@ class ChangesetDiff extends React.Component { defaultCollapse={defaultCollapse} sideBySide={false} fileControlFactory={fileControlFactory} + stickyHeader={true} /> ); } diff --git a/scm-ui/ui-webapp/src/repos/compare/CompareView.tsx b/scm-ui/ui-webapp/src/repos/compare/CompareView.tsx index 8c8b512664..369507cd44 100644 --- a/scm-ui/ui-webapp/src/repos/compare/CompareView.tsx +++ b/scm-ui/ui-webapp/src/repos/compare/CompareView.tsx @@ -54,7 +54,7 @@ const CompareRoutes: FC = ({ repository, baseUrl }) => { - +