mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 15:35:49 +01:00
initial implementation
This commit is contained in:
@@ -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));
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user