mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 14:35:45 +01:00
merge with 2.0.0-m3
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { AsyncCreatable, Async } from "react-select";
|
||||
import type { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
|
||||
import {Async, AsyncCreatable} from "react-select";
|
||||
import type {AutocompleteObject, SelectValue} from "@scm-manager/ui-types";
|
||||
import LabelWithHelpIcon from "./forms/LabelWithHelpIcon";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -25,12 +25,17 @@ const styles = {
|
||||
},
|
||||
content: {
|
||||
display: "flex",
|
||||
flexGrow: 1
|
||||
flexGrow: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between"
|
||||
},
|
||||
footer: {
|
||||
display: "flex",
|
||||
marginTop: "auto",
|
||||
paddingBottom: "1.5rem"
|
||||
},
|
||||
noBottomMargin: {
|
||||
marginBottom: "0 !important"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -38,24 +43,37 @@ type Props = {
|
||||
title: string,
|
||||
description: string,
|
||||
avatar: React.Node,
|
||||
contentRight?: React.Node,
|
||||
footerLeft: React.Node,
|
||||
footerRight: React.Node,
|
||||
link: string,
|
||||
link?: string,
|
||||
action?: () => void,
|
||||
|
||||
// context props
|
||||
classes: any
|
||||
};
|
||||
|
||||
class CardColumn extends React.Component<Props> {
|
||||
createLink = () => {
|
||||
const { link } = this.props;
|
||||
const { link, action } = this.props;
|
||||
if (link) {
|
||||
return <Link className="overlay-column" to={link} />;
|
||||
} else if (action) {
|
||||
return <a className="overlay-column" onClick={e => {e.preventDefault(); action();}} href="#" />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { avatar, title, description, footerLeft, footerRight, classes } = this.props;
|
||||
const {
|
||||
avatar,
|
||||
title,
|
||||
description,
|
||||
contentRight,
|
||||
footerLeft,
|
||||
footerRight,
|
||||
classes
|
||||
} = this.props;
|
||||
const link = this.createLink();
|
||||
return (
|
||||
<>
|
||||
@@ -64,16 +82,29 @@ class CardColumn extends React.Component<Props> {
|
||||
<figure className={classNames(classes.centerImage, "media-left")}>
|
||||
{avatar}
|
||||
</figure>
|
||||
<div className={classNames("media-content", "text-box", classes.flexFullHeight)}>
|
||||
<div
|
||||
className={classNames(
|
||||
"media-content",
|
||||
"text-box",
|
||||
classes.flexFullHeight
|
||||
)}
|
||||
>
|
||||
<div className={classes.content}>
|
||||
<div className="content shorten-text">
|
||||
<div
|
||||
className={classNames(
|
||||
"content",
|
||||
"shorten-text",
|
||||
classes.noBottomMargin
|
||||
)}
|
||||
>
|
||||
<p className="is-marginless">
|
||||
<strong>{title}</strong>
|
||||
</p>
|
||||
<p className="shorten-text">{description}</p>
|
||||
</div>
|
||||
{contentRight && contentRight}
|
||||
</div>
|
||||
<div className={classNames(classes.footer, "level")}>
|
||||
<div className={classNames("level", classes.footer)}>
|
||||
<div className="level-left is-hidden-mobile">{footerLeft}</div>
|
||||
<div className="level-right is-mobile">{footerRight}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { BackendError, ForbiddenError, UnauthorizedError } from "./errors";
|
||||
import {translate} from "react-i18next";
|
||||
import {BackendError, ForbiddenError, UnauthorizedError} from "./errors";
|
||||
import Notification from "./Notification";
|
||||
import BackendErrorNotification from "./BackendErrorNotification";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import {translate} from "react-i18next";
|
||||
import type AutocompleteProps from "./UserGroupAutocomplete";
|
||||
import UserGroupAutocomplete from "./UserGroupAutocomplete";
|
||||
|
||||
|
||||
@@ -2,11 +2,18 @@
|
||||
import * as React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type NotificationType = "primary" | "info" | "success" | "warning" | "danger";
|
||||
type NotificationType =
|
||||
| "primary"
|
||||
| "info"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "inherit";
|
||||
|
||||
type Props = {
|
||||
type: NotificationType,
|
||||
onClose?: () => void,
|
||||
className?: string,
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
@@ -24,9 +31,12 @@ class Notification extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { type, children } = this.props;
|
||||
const { type, className, children } = this.props;
|
||||
|
||||
const color = type !== "inherit" ? "is-" + type : "";
|
||||
|
||||
return (
|
||||
<div className={classNames("notification", "is-" + type)}>
|
||||
<div className={classNames("notification", color, className)}>
|
||||
{this.renderCloseButton()}
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import {translate} from "react-i18next";
|
||||
import type AutocompleteProps from "./UserGroupAutocomplete";
|
||||
import UserGroupAutocomplete from "./UserGroupAutocomplete";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import type { SelectValue } from "@scm-manager/ui-types";
|
||||
import type {SelectValue} from "@scm-manager/ui-types";
|
||||
import Autocomplete from "./Autocomplete";
|
||||
|
||||
export type AutocompleteProps = {
|
||||
|
||||
@@ -14,7 +14,7 @@ class ButtonGroup extends React.Component<Props> {
|
||||
const childWrapper = [];
|
||||
React.Children.forEach(children, child => {
|
||||
if (child) {
|
||||
childWrapper.push(<p className="control">{child}</p>);
|
||||
childWrapper.push(<p className="control" key={childWrapper.length}>{child}</p>);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import DiffFile from "./DiffFile";
|
||||
import type { DiffObjectProps } from "./DiffTypes";
|
||||
import type {DiffObjectProps, File} from "./DiffTypes";
|
||||
|
||||
type Props = DiffObjectProps & {
|
||||
diff: any
|
||||
diff: File[]
|
||||
};
|
||||
|
||||
class Diff extends React.Component<Props> {
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {
|
||||
Hunk,
|
||||
Diff as DiffComponent,
|
||||
getChangeKey,
|
||||
Change,
|
||||
DiffObjectProps,
|
||||
File
|
||||
} from "react-diff-view";
|
||||
import {Change, Diff as DiffComponent, DiffObjectProps, File, getChangeKey, Hunk} from "react-diff-view";
|
||||
import injectSheets from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import { translate } from "react-i18next";
|
||||
import { ButtonGroup, Button } from "../buttons";
|
||||
import {translate} from "react-i18next";
|
||||
import {Button, ButtonGroup} from "../buttons";
|
||||
|
||||
const styles = {
|
||||
panel: {
|
||||
@@ -46,6 +39,7 @@ const styles = {
|
||||
|
||||
type Props = DiffObjectProps & {
|
||||
file: File,
|
||||
collapsible: true,
|
||||
// context props
|
||||
classes: any,
|
||||
t: string => string
|
||||
@@ -66,9 +60,11 @@ class DiffFile extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
toggleCollapse = () => {
|
||||
this.setState(state => ({
|
||||
collapsed: !state.collapsed
|
||||
}));
|
||||
if (this.props.collapsable) {
|
||||
this.setState(state => ({
|
||||
collapsed: !state.collapsed
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
toggleSideBySide = () => {
|
||||
@@ -173,6 +169,9 @@ class DiffFile extends React.Component<Props, State> {
|
||||
|
||||
renderChangeTag = (file: any) => {
|
||||
const { t, classes } = this.props;
|
||||
if (!file.type) {
|
||||
return;
|
||||
}
|
||||
const key = "diff.changes." + file.type;
|
||||
let value = t(key);
|
||||
if (key === value) {
|
||||
@@ -205,6 +204,7 @@ class DiffFile extends React.Component<Props, State> {
|
||||
file,
|
||||
fileControlFactory,
|
||||
fileAnnotationFactory,
|
||||
collapsible,
|
||||
classes,
|
||||
t
|
||||
} = this.props;
|
||||
@@ -227,6 +227,7 @@ class DiffFile extends React.Component<Props, State> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const collapseIcon = collapsible? <i className={icon} />: null;
|
||||
|
||||
const fileControls = fileControlFactory
|
||||
? fileControlFactory(file, this.setCollapse)
|
||||
@@ -240,7 +241,7 @@ class DiffFile extends React.Component<Props, State> {
|
||||
onClick={this.toggleCollapse}
|
||||
title={this.hoverFileTitle(file)}
|
||||
>
|
||||
<i className={icon} />
|
||||
{collapseIcon}
|
||||
<span
|
||||
className={classNames("is-ellipsis-overflow", classes.title)}
|
||||
>
|
||||
|
||||
@@ -27,12 +27,17 @@ export type Hunk = {
|
||||
content: string
|
||||
};
|
||||
|
||||
export type ChangeType = "insert" | "delete" | "normal";
|
||||
|
||||
export type Change = {
|
||||
content: string,
|
||||
isNormal: boolean,
|
||||
newLineNumber: number,
|
||||
oldLineNumber: number,
|
||||
type: string
|
||||
isNormal?: boolean,
|
||||
isInsert?: boolean,
|
||||
isDelete?: boolean,
|
||||
lineNumber?: number,
|
||||
newLineNumber?: number,
|
||||
oldLineNumber?: number,
|
||||
type: ChangeType
|
||||
};
|
||||
|
||||
export type BaseContext = {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { apiClient } from "../apiclient";
|
||||
import {apiClient} from "../apiclient";
|
||||
import ErrorNotification from "../ErrorNotification";
|
||||
import parser from "gitdiff-parser";
|
||||
|
||||
import Loading from "../Loading";
|
||||
import Diff from "./Diff";
|
||||
import type {DiffObjectProps} from "./DiffTypes";
|
||||
import type {DiffObjectProps, File} from "./DiffTypes";
|
||||
|
||||
type Props = DiffObjectProps & {
|
||||
url: string
|
||||
};
|
||||
|
||||
type State = {
|
||||
diff?: any,
|
||||
diff?: File[],
|
||||
loading: boolean,
|
||||
error?: Error
|
||||
};
|
||||
@@ -47,7 +47,8 @@ class LoadingDiff extends React.Component<Props, State> {
|
||||
.get(url)
|
||||
.then(response => response.text())
|
||||
.then(parser.parse)
|
||||
.then(diff => {
|
||||
// $FlowFixMe
|
||||
.then((diff: File[]) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
diff: diff
|
||||
|
||||
@@ -21,7 +21,7 @@ class ChangesetButtonGroup extends React.Component<Props> {
|
||||
const sourcesLink = createSourcesLink(repository, changeset);
|
||||
|
||||
return (
|
||||
<ButtonAddons className="is-pulled-right">
|
||||
<ButtonAddons className="is-marginless">
|
||||
<Button link={changesetLink} className="reduced-mobile">
|
||||
<span className="icon">
|
||||
<i className="fas fa-exchange-alt" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Changeset, Repository, Tag } from "@scm-manager/ui-types";
|
||||
import type { Changeset, Repository } from "@scm-manager/ui-types";
|
||||
|
||||
import classNames from "classnames";
|
||||
import { Interpolate, translate } from "react-i18next";
|
||||
@@ -26,21 +26,22 @@ const styles = {
|
||||
},
|
||||
avatarFigure: {
|
||||
marginTop: ".25rem",
|
||||
marginRight: ".5rem",
|
||||
marginRight: ".5rem"
|
||||
},
|
||||
avatarImage: {
|
||||
height: "35px",
|
||||
width: "35px"
|
||||
},
|
||||
isVcentered: {
|
||||
marginTop: "auto",
|
||||
marginBottom: "auto"
|
||||
},
|
||||
metadata: {
|
||||
marginLeft: 0
|
||||
},
|
||||
tag: {
|
||||
marginTop: ".5rem"
|
||||
isVcentered: {
|
||||
alignSelf: "center"
|
||||
},
|
||||
flexVcenter: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -65,52 +66,65 @@ class ChangesetRow extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<div className={classes.changeset}>
|
||||
<div className="columns">
|
||||
<div className="columns is-gapless is-mobile">
|
||||
<div className="column is-three-fifths">
|
||||
|
||||
<h4 className="has-text-weight-bold is-ellipsis-overflow">
|
||||
<ExtensionPoint
|
||||
name="changeset.description"
|
||||
props={{ changeset, value: description.title }}
|
||||
renderAll={false}
|
||||
>
|
||||
{description.title}
|
||||
</ExtensionPoint>
|
||||
</h4>
|
||||
|
||||
<div className="media">
|
||||
<AvatarWrapper>
|
||||
<figure className={classNames(classes.avatarFigure, "media-left")}>
|
||||
<div className={classNames("image", classes.avatarImage)}>
|
||||
<AvatarImage person={changeset.author} />
|
||||
<div className="columns is-gapless">
|
||||
<div className="column is-four-fifths">
|
||||
<h4 className="has-text-weight-bold is-ellipsis-overflow">
|
||||
<ExtensionPoint
|
||||
name="changeset.description"
|
||||
props={{ changeset, value: description.title }}
|
||||
renderAll={false}
|
||||
>
|
||||
{description.title}
|
||||
</ExtensionPoint>
|
||||
</h4>
|
||||
<div className="media">
|
||||
<AvatarWrapper>
|
||||
<figure
|
||||
className={classNames(classes.avatarFigure, "media-left")}
|
||||
>
|
||||
<div className={classNames("image", classes.avatarImage)}>
|
||||
<AvatarImage person={changeset.author} />
|
||||
</div>
|
||||
</figure>
|
||||
</AvatarWrapper>
|
||||
<div className={classNames(classes.metadata, "media-right")}>
|
||||
<p className="is-hidden-touch">
|
||||
<Interpolate
|
||||
i18nKey="changeset.summary"
|
||||
id={changesetId}
|
||||
time={dateFromNow}
|
||||
/>
|
||||
</p>
|
||||
<p className="is-hidden-desktop">
|
||||
<Interpolate
|
||||
i18nKey="changeset.shortSummary"
|
||||
id={changesetId}
|
||||
time={dateFromNow}
|
||||
/>
|
||||
</p>
|
||||
<p className="is-size-7">
|
||||
<ChangesetAuthor changeset={changeset} />
|
||||
</p>
|
||||
</div>
|
||||
</figure>
|
||||
</AvatarWrapper>
|
||||
<div className={classNames(classes.metadata, "media-right")}>
|
||||
<p className="is-hidden-mobile is-hidden-tablet-only">
|
||||
<Interpolate
|
||||
i18nKey="changeset.summary"
|
||||
id={changesetId}
|
||||
time={dateFromNow}
|
||||
/>
|
||||
</p>
|
||||
<p className="is-hidden-desktop">
|
||||
<Interpolate
|
||||
i18nKey="changeset.shortSummary"
|
||||
id={changesetId}
|
||||
time={dateFromNow}
|
||||
/>
|
||||
</p>
|
||||
<p className="is-size-7">
|
||||
<ChangesetAuthor changeset={changeset} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classNames("column", classes.isVcentered)}>
|
||||
<ChangesetTags changeset={changeset} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={classNames("column", classes.isVcentered)}>
|
||||
<ChangesetTags changeset={changeset} />
|
||||
<ChangesetButtonGroup repository={repository} changeset={changeset} />
|
||||
<div className={classNames("column", classes.flexVcenter)}>
|
||||
<ChangesetButtonGroup
|
||||
repository={repository}
|
||||
changeset={changeset}
|
||||
/>
|
||||
<ExtensionPoint
|
||||
name="changeset.right"
|
||||
props={{ repository, changeset }}
|
||||
renderAll={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,9 +4,6 @@ import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
|
||||
const styles = {
|
||||
tag: {
|
||||
marginTop: ".5rem"
|
||||
},
|
||||
spacing: {
|
||||
marginRight: ".25rem"
|
||||
}
|
||||
@@ -24,7 +21,7 @@ class ChangesetTagBase extends React.Component<Props> {
|
||||
render() {
|
||||
const { icon, label, classes } = this.props;
|
||||
return (
|
||||
<span className={classNames(classes.tag, "tag", "is-info")}>
|
||||
<span className={classNames("tag", "is-info")}>
|
||||
<span className={classNames("fa", icon, classes.spacing)} /> {label}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// @flow
|
||||
import * as diffs from "./diffs";
|
||||
|
||||
export { diffs };
|
||||
|
||||
export * from "./changesets";
|
||||
|
||||
export { default as Diff } from "./Diff";
|
||||
export { default as DiffFile } from "./DiffFile";
|
||||
export { default as LoadingDiff } from "./LoadingDiff";
|
||||
|
||||
export type {
|
||||
@@ -12,6 +14,7 @@ export type {
|
||||
FileChangeType,
|
||||
Hunk,
|
||||
Change,
|
||||
ChangeType,
|
||||
BaseContext,
|
||||
AnnotationFactory,
|
||||
AnnotationFactoryContext,
|
||||
|
||||
Reference in New Issue
Block a user