initial implementation

This commit is contained in:
Konstantin Schaper
2020-08-21 18:27:48 +02:00
parent 330f7c500e
commit 8f3c0dce1e
2 changed files with 62 additions and 7 deletions

View File

@@ -22,34 +22,75 @@
* SOFTWARE. * SOFTWARE.
*/ */
import React from "react"; import React from "react";
import DiffFile from "./DiffFile"; import DiffFile, {escapeWhitespace} from "./DiffFile";
import { DiffObjectProps, File, FileControlFactory } from "./DiffTypes"; import { DiffObjectProps, File, FileControlFactory } from "./DiffTypes";
import Notification from "../Notification"; import Notification from "../Notification";
import { WithTranslation, withTranslation } from "react-i18next"; import { WithTranslation, withTranslation } from "react-i18next";
import {RouteComponentProps, withRouter} from "react-router-dom";
type Props = WithTranslation & type Props = RouteComponentProps & WithTranslation &
DiffObjectProps & { DiffObjectProps & {
diff: File[]; diff: File[];
fileControlFactory?: FileControlFactory; fileControlFactory?: FileControlFactory;
}; };
class Diff extends React.Component<Props> { type State = {
contentRef?: HTMLElement;
}
function getAnchorSelector(uriHashContent: string) {
return "#" + escapeWhitespace(decodeURIComponent(uriHashContent));
}
class Diff extends React.Component<Props, State> {
static defaultProps: Partial<Props> = { static defaultProps: Partial<Props> = {
sideBySide: false sideBySide: false
}; };
constructor(props: Readonly<Props>) {
super(props);
this.state = {
contentRef: undefined
};
}
componentDidUpdate() {
const { contentRef } = this.state;
// we have to use componentDidUpdate, because we have to wait until all
// children are rendered and componentDidMount is called before the
// changeset content was rendered.
const hash = this.props.location.hash;
const match = hash && hash.match(/^#diff-(.*)$/);
if (contentRef && match) {
const selector = getAnchorSelector(match[1]);
const element = contentRef.querySelector(selector);
if (element && element.scrollIntoView) {
element.scrollIntoView();
}
}
}
render() { render() {
const { diff, t, ...fileProps } = this.props; const { diff, t, ...fileProps } = this.props;
const updateContentRef = (el: HTMLElement | null) => {
if (el !== null && this.state.contentRef === undefined) {
this.setState({ contentRef: el });
}
};
return ( return (
<> <div
ref={updateContentRef}
>
{diff.length === 0 ? ( {diff.length === 0 ? (
<Notification type="info">{t("diff.noDiffFound")}</Notification> <Notification type="info">{t("diff.noDiffFound")}</Notification>
) : ( ) : (
diff.map((file, index) => <DiffFile key={index} file={file} {...fileProps} {...this.props} />) diff.map((file, index) => <DiffFile key={index} file={file} {...fileProps} {...this.props} />)
)} )}
</> </div>
); );
} }
} }
export default withTranslation("repos")(Diff); export default withRouter(withTranslation("repos")(Diff));

View File

@@ -90,6 +90,10 @@ const ChangeTypeTag = styled(Tag)`
margin-left: 0.75rem; margin-left: 0.75rem;
`; `;
export function escapeWhitespace(path: string) {
return path.toLowerCase().replace(/\W/g, "-");
}
class DiffFile extends React.Component<Props, State> { class DiffFile extends React.Component<Props, State> {
static defaultProps: Partial<Props> = { static defaultProps: Partial<Props> = {
defaultCollapse: false, defaultCollapse: false,
@@ -350,6 +354,16 @@ class DiffFile extends React.Component<Props, State> {
} }
}; };
getAnchorId(file: File) {
let path: string;
if (file.type === "delete") {
path = file.oldPath;
} else {
path = file.newPath;
}
return escapeWhitespace(path);
}
renderFileTitle = (file: File) => { renderFileTitle = (file: File) => {
if (file.oldPath !== file.newPath && (file.type === "copy" || file.type === "rename")) { if (file.oldPath !== file.newPath && (file.type === "copy" || file.type === "rename")) {
return ( return (
@@ -454,7 +468,7 @@ class DiffFile extends React.Component<Props, State> {
} }
return ( return (
<DiffFilePanel className={classNames("panel", "is-size-6")} collapsed={(file && file.isBinary) || collapsed}> <DiffFilePanel className={classNames("panel", "is-size-6")} collapsed={(file && file.isBinary) || collapsed} id={this.getAnchorId(file)}>
{errorModal} {errorModal}
<div className="panel-heading"> <div className="panel-heading">
<FlexWrapLevel className="level"> <FlexWrapLevel className="level">