mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 15:05:44 +01:00
merged
This commit is contained in:
@@ -79,9 +79,11 @@ final class GitHunkParser {
|
||||
++oldLineCounter;
|
||||
break;
|
||||
default:
|
||||
if (!line.equals("\\ No newline at end of file")) {
|
||||
throw new IllegalStateException("cannot handle diff line: " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class AddedGitDiffLine implements DiffLine {
|
||||
private final int newLineNumber;
|
||||
|
||||
@@ -68,6 +68,17 @@ class GitHunkParserTest {
|
||||
" a\n" +
|
||||
"~illegal line\n";
|
||||
|
||||
private static final String NO_NEWLINE_DIFF = "diff --git a/.editorconfig b/.editorconfig\n" +
|
||||
"index ea2a3ba..2f02f32 100644\n" +
|
||||
"--- a/.editorconfig\n" +
|
||||
"+++ b/.editorconfig\n" +
|
||||
"@@ -10,3 +10,4 @@\n" +
|
||||
" indent_style = space\n" +
|
||||
" indent_size = 2\n" +
|
||||
" charset = utf-8\n" +
|
||||
"+added line\n" +
|
||||
"\\ No newline at end of file\n";
|
||||
|
||||
@Test
|
||||
void shouldParseHunks() {
|
||||
List<Hunk> hunks = new GitHunkParser().parse(DIFF_001);
|
||||
@@ -127,6 +138,27 @@ class GitHunkParserTest {
|
||||
assertThrows(IllegalStateException.class, () -> new GitHunkParser().parse(ILLEGAL_DIFF));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreNoNewlineLine() {
|
||||
List<Hunk> hunks = new GitHunkParser().parse(NO_NEWLINE_DIFF);
|
||||
|
||||
Hunk hunk = hunks.get(0);
|
||||
|
||||
Iterator<DiffLine> lines = hunk.iterator();
|
||||
|
||||
DiffLine line1 = lines.next();
|
||||
assertThat(line1.getOldLineNumber()).hasValue(10);
|
||||
assertThat(line1.getNewLineNumber()).hasValue(10);
|
||||
assertThat(line1.getContent()).isEqualTo("indent_style = space");
|
||||
|
||||
lines.next();
|
||||
lines.next();
|
||||
DiffLine lastLine = lines.next();
|
||||
assertThat(lastLine.getOldLineNumber()).isEmpty();
|
||||
assertThat(lastLine.getNewLineNumber()).hasValue(13);
|
||||
assertThat(lastLine.getContent()).isEqualTo("added line");
|
||||
}
|
||||
|
||||
private void assertHunk(Hunk hunk, int oldStart, int oldLineCount, int newStart, int newLineCount) {
|
||||
assertThat(hunk.getOldStart()).isEqualTo(oldStart);
|
||||
assertThat(hunk.getOldLineCount()).isEqualTo(oldLineCount);
|
||||
|
||||
@@ -5,15 +5,15 @@ import { translate } from "react-i18next";
|
||||
import { InputField, Checkbox } from "@scm-manager/ui-components";
|
||||
|
||||
type Configuration = {
|
||||
"hgBinary": string,
|
||||
"pythonBinary": string,
|
||||
"pythonPath"?: string,
|
||||
"encoding": string,
|
||||
"useOptimizedBytecode": boolean,
|
||||
"showRevisionInId": boolean,
|
||||
"disableHookSSLValidation": boolean,
|
||||
"enableHttpPostArgs": boolean,
|
||||
"_links": Links
|
||||
hgBinary: string,
|
||||
pythonBinary: string,
|
||||
pythonPath?: string,
|
||||
encoding: string,
|
||||
useOptimizedBytecode: boolean,
|
||||
showRevisionInId: boolean,
|
||||
disableHookSSLValidation: boolean,
|
||||
enableHttpPostArgs: boolean,
|
||||
_links: Links
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@@ -23,24 +23,21 @@ type Props = {
|
||||
onConfigurationChange: (Configuration, boolean) => void,
|
||||
|
||||
// context props
|
||||
t: (string) => string
|
||||
}
|
||||
t: string => string
|
||||
};
|
||||
|
||||
type State = Configuration & {
|
||||
validationErrors: string[]
|
||||
};
|
||||
|
||||
class HgConfigurationForm extends React.Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { ...props.initialConfiguration, validationErrors: [] };
|
||||
}
|
||||
|
||||
updateValidationStatus = () => {
|
||||
const requiredFields = [
|
||||
"hgBinary", "pythonBinary", "encoding"
|
||||
];
|
||||
const requiredFields = ["hgBinary", "pythonBinary", "encoding"];
|
||||
|
||||
const validationErrors = [];
|
||||
for (let field of requiredFields) {
|
||||
@@ -56,20 +53,28 @@ class HgConfigurationForm extends React.Component<Props, State> {
|
||||
return validationErrors.length === 0;
|
||||
};
|
||||
|
||||
|
||||
hasValidationError = (name: string) => {
|
||||
return this.state.validationErrors.indexOf(name) >= 0;
|
||||
};
|
||||
|
||||
handleChange = (value: any, name: string) => {
|
||||
this.setState({
|
||||
this.setState(
|
||||
{
|
||||
[name]: value
|
||||
}, () => this.props.onConfigurationChange(this.state, this.updateValidationStatus()));
|
||||
},
|
||||
() =>
|
||||
this.props.onConfigurationChange(
|
||||
this.state,
|
||||
this.updateValidationStatus()
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
inputField = (name: string) => {
|
||||
const { readOnly, t } = this.props;
|
||||
return <InputField
|
||||
return (
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
name={name}
|
||||
label={t("scm-hg-plugin.config." + name)}
|
||||
helpText={t("scm-hg-plugin.config." + name + "HelpText")}
|
||||
@@ -78,36 +83,43 @@ class HgConfigurationForm extends React.Component<Props, State> {
|
||||
validationError={this.hasValidationError(name)}
|
||||
errorMessage={t("scm-hg-plugin.config.required")}
|
||||
disabled={readOnly}
|
||||
/>;
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
checkbox = (name: string) => {
|
||||
const { readOnly, t } = this.props;
|
||||
return <Checkbox
|
||||
return (
|
||||
<Checkbox
|
||||
name={name}
|
||||
label={t("scm-hg-plugin.config." + name)}
|
||||
helpText={t("scm-hg-plugin.config." + name + "HelpText")}
|
||||
checked={this.state[name]}
|
||||
onChange={this.handleChange}
|
||||
disabled={readOnly}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className="columns is-multiline">
|
||||
{this.inputField("hgBinary")}
|
||||
{this.inputField("pythonBinary")}
|
||||
{this.inputField("pythonPath")}
|
||||
{this.inputField("encoding")}
|
||||
<div className="column is-half">
|
||||
{this.checkbox("useOptimizedBytecode")}
|
||||
{this.checkbox("showRevisionInId")}
|
||||
</div>
|
||||
<div className="column is-half">
|
||||
{this.checkbox("disableHookSSLValidation")}
|
||||
{this.checkbox("enableHttpPostArgs")}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default translate("plugins")(HgConfigurationForm);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import Tooltip from './Tooltip';
|
||||
import HelpIcon from './HelpIcon';
|
||||
import classNames from "classnames";
|
||||
import Tooltip from "./Tooltip";
|
||||
import HelpIcon from "./HelpIcon";
|
||||
|
||||
const styles = {
|
||||
tooltip: {
|
||||
@@ -14,21 +15,22 @@ const styles = {
|
||||
|
||||
type Props = {
|
||||
message: string,
|
||||
className?: string,
|
||||
classes: any
|
||||
}
|
||||
};
|
||||
|
||||
class Help extends React.Component<Props> {
|
||||
|
||||
render() {
|
||||
const { message, classes } = this.props;
|
||||
const { message, className, classes } = this.props;
|
||||
return (
|
||||
<Tooltip className={classes.tooltip} message={message}>
|
||||
<Tooltip
|
||||
className={classNames(classes.tooltip, className)}
|
||||
message={message}
|
||||
>
|
||||
<HelpIcon />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(Help);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import Icon from "./Icon";
|
||||
|
||||
type Props = {
|
||||
classes: any
|
||||
@@ -16,7 +16,9 @@ const styles = {
|
||||
class HelpIcon extends React.Component<Props> {
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return <i className={classNames("fa fa-question-circle has-text-info", classes.textinfo)}></i>;
|
||||
return (
|
||||
<Icon className={classes.textinfo} name="question-circle" />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,22 +4,26 @@ import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
title?: string,
|
||||
name: string
|
||||
}
|
||||
name: string,
|
||||
color: string,
|
||||
className?: string
|
||||
};
|
||||
|
||||
export default class Icon extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
color: "grey-light"
|
||||
};
|
||||
|
||||
render() {
|
||||
const { title, name } = this.props;
|
||||
const { title, name, color, className } = this.props;
|
||||
if (title) {
|
||||
return (
|
||||
<i title={title} className={classNames("is-icon", "fas", "fa-fw", "fa-" + name)}/>
|
||||
<i
|
||||
title={title}
|
||||
className={classNames("fas", "fa-fw", "fa-" + name, `has-text-${color}`, className)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<i className={classNames("is-icon", "fas", "fa-" + name)}/>
|
||||
);
|
||||
return <i className={classNames("fas", "fa-" + name, `has-text-${color}`, className)} />;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import SyntaxHighlighter from "./SyntaxHighlighter";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import injectSheet from "react-jss";
|
||||
import Markdown from "react-markdown/with-html";
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import SyntaxHighlighter from "./SyntaxHighlighter";
|
||||
import MarkdownHeadingRenderer from "./MarkdownHeadingRenderer";
|
||||
import { withRouter } from "react-router-dom";
|
||||
|
||||
|
||||
type Props = {
|
||||
content: string,
|
||||
@@ -14,11 +14,34 @@ type Props = {
|
||||
enableAnchorHeadings: boolean,
|
||||
|
||||
// context props
|
||||
classes: any,
|
||||
location: any
|
||||
};
|
||||
|
||||
class MarkdownView extends React.Component<Props> {
|
||||
const styles = {
|
||||
markdown: {
|
||||
"& > .content": {
|
||||
"& > h1, h2, h3, h4, h5, h6": {
|
||||
margin: "0.5rem 0",
|
||||
fontSize: "0.9rem"
|
||||
},
|
||||
"& > h1": {
|
||||
fontWeight: "700"
|
||||
},
|
||||
"& > h2": {
|
||||
fontWeight: "600"
|
||||
},
|
||||
"& > h3, h4, h5, h6": {
|
||||
fontWeight: "500"
|
||||
},
|
||||
"& strong": {
|
||||
fontWeight: "500"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MarkdownView extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
enableAnchorHeadings: false
|
||||
};
|
||||
@@ -45,7 +68,13 @@ class MarkdownView extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {content, renderers, renderContext, enableAnchorHeadings} = this.props;
|
||||
const {
|
||||
content,
|
||||
renderers,
|
||||
renderContext,
|
||||
enableAnchorHeadings,
|
||||
classes
|
||||
} = this.props;
|
||||
|
||||
const rendererFactory = binder.getExtension("markdown-renderer-factory");
|
||||
let rendererList = renderers;
|
||||
@@ -67,7 +96,7 @@ class MarkdownView extends React.Component<Props> {
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={el => (this.contentRef = el)}>
|
||||
<div className={classes.markdown} ref={el => (this.contentRef = el)}>
|
||||
<Markdown
|
||||
className="content"
|
||||
skipHtml={true}
|
||||
@@ -80,4 +109,4 @@ class MarkdownView extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(MarkdownView);
|
||||
export default injectSheet(styles)(withRouter(MarkdownView));
|
||||
|
||||
60
scm-ui-components/packages/ui-components/src/Tag.js
Normal file
60
scm-ui-components/packages/ui-components/src/Tag.js
Normal file
@@ -0,0 +1,60 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
color: string,
|
||||
icon?: string,
|
||||
label: string,
|
||||
title?: string,
|
||||
onClick?: () => void,
|
||||
onRemove?: () => void
|
||||
};
|
||||
|
||||
class Tag extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
color: "light"
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
color,
|
||||
icon,
|
||||
label,
|
||||
title,
|
||||
onClick,
|
||||
onRemove
|
||||
} = this.props;
|
||||
let showIcon = null;
|
||||
if (icon) {
|
||||
showIcon = (
|
||||
<>
|
||||
<i className={classNames("fas", `fa-${icon}`)} />
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
let showDelete = null;
|
||||
if (onRemove) {
|
||||
showDelete = <a className="tag is-delete" onClick={onRemove} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
className={classNames("tag", `is-${color}`, className)}
|
||||
title={title}
|
||||
onClick={onClick}
|
||||
>
|
||||
{showIcon}
|
||||
{label}
|
||||
</span>
|
||||
{showDelete}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Tag;
|
||||
@@ -4,7 +4,7 @@ import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class AddButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="default" {...this.props} />;
|
||||
return <Button color="default" icon="plus" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import * as React from "react";
|
||||
import classNames from "classnames";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import Icon from "../Icon";
|
||||
|
||||
export type ButtonProps = {
|
||||
label?: string,
|
||||
@@ -9,8 +10,10 @@ export type ButtonProps = {
|
||||
disabled?: boolean,
|
||||
action?: (event: Event) => void,
|
||||
link?: string,
|
||||
fullWidth?: boolean,
|
||||
className?: string,
|
||||
icon?: string,
|
||||
fullWidth?: boolean,
|
||||
reducedMobile?: boolean,
|
||||
children?: React.Node,
|
||||
|
||||
// context props
|
||||
@@ -47,12 +50,40 @@ class Button extends React.Component<Props> {
|
||||
disabled,
|
||||
type,
|
||||
color,
|
||||
className,
|
||||
icon,
|
||||
fullWidth,
|
||||
children,
|
||||
className
|
||||
reducedMobile,
|
||||
children
|
||||
} = this.props;
|
||||
const loadingClass = loading ? "is-loading" : "";
|
||||
const fullWidthClass = fullWidth ? "is-fullwidth" : "";
|
||||
const reducedMobileClass = reducedMobile ? "is-reduced-mobile" : "";
|
||||
if (icon) {
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
onClick={this.onClick}
|
||||
className={classNames(
|
||||
"button",
|
||||
"is-" + color,
|
||||
loadingClass,
|
||||
fullWidthClass,
|
||||
reducedMobileClass,
|
||||
className
|
||||
)}
|
||||
>
|
||||
<span className="icon is-medium">
|
||||
<Icon name={icon} color="inherit" />
|
||||
</span>
|
||||
<span>
|
||||
{label} {children}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
@@ -69,8 +100,7 @@ class Button extends React.Component<Props> {
|
||||
{label} {children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Button);
|
||||
|
||||
@@ -14,7 +14,7 @@ class ButtonAddons 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>);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class DeleteButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="warning" {...this.props} />;
|
||||
return <Button color="warning" icon="times" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class DownloadButton extends React.Component<Props> {
|
||||
const { displayName, url, disabled, onClick } = this.props;
|
||||
const onClickOrDefault = !!onClick ? onClick : () => {};
|
||||
return (
|
||||
<a className="button is-large is-link" href={url} disabled={disabled} onClick={onClickOrDefault}>
|
||||
<a className="button is-link" href={url} disabled={disabled} onClick={onClickOrDefault}>
|
||||
<span className="icon is-medium">
|
||||
<i className="fas fa-arrow-circle-down" />
|
||||
</span>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import RemoveEntryOfTableButton from "../buttons/RemoveEntryOfTableButton";
|
||||
|
||||
type Props = {
|
||||
members: string[],
|
||||
t: string => string,
|
||||
memberListChanged: (string[]) => void
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
||||
class MemberNameTable extends React.Component<Props, State> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<table className="table is-hoverable is-fullwidth">
|
||||
<tbody>
|
||||
{this.props.members.map(member => {
|
||||
return (
|
||||
<tr key={member}>
|
||||
<td key={member}>{member}</td>
|
||||
<td>
|
||||
<RemoveEntryOfTableButton
|
||||
entryname={member}
|
||||
removeEntry={this.removeEntry}
|
||||
disabled={false}
|
||||
label={t("remove-member-button.label")}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
removeEntry = (membername: string) => {
|
||||
const newMembers = this.props.members.filter(name => name !== membername);
|
||||
this.props.memberListChanged(newMembers);
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("groups")(MemberNameTable);
|
||||
@@ -0,0 +1,37 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { DisplayedUser } from "@scm-manager/ui-types";
|
||||
import TagGroup from "./TagGroup";
|
||||
|
||||
type Props = {
|
||||
members: string[],
|
||||
memberListChanged: (string[]) => void,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class MemberNameTagGroup extends React.Component<Props> {
|
||||
render() {
|
||||
const { members, t } = this.props;
|
||||
const membersExtended = members.map(id => {
|
||||
return { id, displayName: id, mail: "" };
|
||||
});
|
||||
return (
|
||||
<TagGroup
|
||||
items={membersExtended}
|
||||
label={t("group.members")}
|
||||
helpText={t("groupForm.help.memberHelpText")}
|
||||
onRemove={this.removeEntry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
removeEntry = (membersExtended: DisplayedUser[]) => {
|
||||
const members = membersExtended.map(function(item) {
|
||||
return item["id"];
|
||||
});
|
||||
this.props.memberListChanged(members);
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("groups")(MemberNameTagGroup);
|
||||
@@ -40,7 +40,8 @@ class PasswordConfirmation extends React.Component<Props, State> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<>
|
||||
<div className="columns is-multiline">
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.newPassword")}
|
||||
type="password"
|
||||
@@ -48,8 +49,9 @@ class PasswordConfirmation extends React.Component<Props, State> {
|
||||
value={this.state.password ? this.state.password : ""}
|
||||
validationError={!this.state.passwordValid}
|
||||
errorMessage={t("password.passwordInvalid")}
|
||||
helpText={t("password.passwordHelpText")}
|
||||
/>
|
||||
</div>
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.confirmPassword")}
|
||||
type="password"
|
||||
@@ -57,9 +59,9 @@ class PasswordConfirmation extends React.Component<Props, State> {
|
||||
value={this.state ? this.state.confirmedPassword : ""}
|
||||
validationError={this.state.passwordConfirmationFailed}
|
||||
errorMessage={t("password.passwordConfirmFailed")}
|
||||
helpText={t("password.passwordConfirmHelpText")}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,7 +101,7 @@ class PasswordConfirmation extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
isValid = () => {
|
||||
return this.state.passwordValid && !this.state.passwordConfirmationFailed
|
||||
return this.state.passwordValid && !this.state.passwordConfirmationFailed;
|
||||
};
|
||||
|
||||
propagateChange = () => {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import type { DisplayedUser } from "@scm-manager/ui-types";
|
||||
import { Help, Tag } from "../index";
|
||||
|
||||
type Props = {
|
||||
items: DisplayedUser[],
|
||||
label: string,
|
||||
helpText?: string,
|
||||
onRemove: (DisplayedUser[]) => void,
|
||||
|
||||
// context props
|
||||
classes: Object
|
||||
};
|
||||
|
||||
const styles = {
|
||||
help: {
|
||||
position: "relative"
|
||||
}
|
||||
};
|
||||
|
||||
class TagGroup extends React.Component<Props> {
|
||||
render() {
|
||||
const { items, label, helpText, classes } = this.props;
|
||||
let help = null;
|
||||
if (helpText) {
|
||||
help = <Help className={classes.help} message={helpText} />;
|
||||
}
|
||||
return (
|
||||
<div className="field is-grouped is-grouped-multiline">
|
||||
{label && items ? (
|
||||
<div className="control">
|
||||
<strong>
|
||||
{label}
|
||||
{help}
|
||||
{items.length > 0 ? ":" : ""}
|
||||
</strong>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{items.map((item, key) => {
|
||||
return (
|
||||
<div className="control" key={key}>
|
||||
<div className="tags has-addons">
|
||||
<Tag
|
||||
color="info is-outlined"
|
||||
label={item.displayName}
|
||||
onRemove={() => this.removeEntry(item)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
removeEntry = item => {
|
||||
const newItems = this.props.items.filter(name => name !== item);
|
||||
this.props.onRemove(newItems);
|
||||
};
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(TagGroup);
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
export { default as AddEntryToTableField } from "./AddEntryToTableField.js";
|
||||
export { default as AutocompleteAddEntryToTableField } from "./AutocompleteAddEntryToTableField.js";
|
||||
export { default as MemberNameTable } from "./MemberNameTable.js";
|
||||
export { default as TagGroup } from "./TagGroup.js";
|
||||
export { default as MemberNameTagGroup } from "./MemberNameTagGroup.js";
|
||||
export { default as Checkbox } from "./Checkbox.js";
|
||||
export { default as Radio } from "./Radio.js";
|
||||
export { default as FilterInput } from "./FilterInput.js";
|
||||
|
||||
@@ -23,6 +23,7 @@ export { default as FileSize } from "./FileSize.js";
|
||||
export { default as ProtectedRoute } from "./ProtectedRoute.js";
|
||||
export { default as Help } from "./Help";
|
||||
export { default as HelpIcon } from "./HelpIcon";
|
||||
export { default as Tag } from "./Tag";
|
||||
export { default as Tooltip } from "./Tooltip";
|
||||
// TODO do we need this? getPageFromMatch is already exported by urls
|
||||
export { getPageFromMatch } from "./urls";
|
||||
|
||||
@@ -18,7 +18,15 @@ class Modal extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { title, closeFunction, body, footer, active, className, headColor } = this.props;
|
||||
const {
|
||||
title,
|
||||
closeFunction,
|
||||
body,
|
||||
footer,
|
||||
active,
|
||||
className,
|
||||
headColor
|
||||
} = this.props;
|
||||
|
||||
const isActive = active ? "is-active" : null;
|
||||
|
||||
@@ -31,7 +39,12 @@ class Modal extends React.Component<Props> {
|
||||
<div className={classNames("modal", className, isActive)}>
|
||||
<div className="modal-background" />
|
||||
<div className="modal-card">
|
||||
<header className={classNames("modal-card-head", `has-background-${headColor}`)}>
|
||||
<header
|
||||
className={classNames(
|
||||
"modal-card-head",
|
||||
`has-background-${headColor}`
|
||||
)}
|
||||
>
|
||||
<p className="modal-card-title is-marginless">{title}</p>
|
||||
<button
|
||||
className="delete"
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {Change, Diff as DiffComponent, DiffObjectProps, File, getChangeKey, Hunk} 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 { Button, ButtonGroup } from "../buttons";
|
||||
import Tag from "../Tag";
|
||||
|
||||
const styles = {
|
||||
panel: {
|
||||
@@ -34,12 +42,35 @@ const styles = {
|
||||
},
|
||||
changeType: {
|
||||
marginLeft: ".75rem"
|
||||
},
|
||||
diff: {
|
||||
/* column sizing */
|
||||
"& > colgroup .diff-gutter-col": {
|
||||
width: "3.25rem"
|
||||
},
|
||||
/* prevent following content from moving down */
|
||||
"& > .diff-gutter:empty:hover::after": {
|
||||
fontSize: "0.7rem"
|
||||
},
|
||||
/* smaller font size for code */
|
||||
"& .diff-line": {
|
||||
fontSize: "0.75rem"
|
||||
},
|
||||
/* comment padding for sideBySide view */
|
||||
"&.split .diff-widget-content .is-indented-line": {
|
||||
paddingLeft: "3.25rem"
|
||||
},
|
||||
/* comment padding for combined view */
|
||||
"&.unified .diff-widget-content .is-indented-line": {
|
||||
paddingLeft: "6.5rem"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
type Props = DiffObjectProps & {
|
||||
file: File,
|
||||
collapsible: true,
|
||||
|
||||
// context props
|
||||
classes: any,
|
||||
t: string => string
|
||||
@@ -179,23 +210,21 @@ class DiffFile extends React.Component<Props, State> {
|
||||
}
|
||||
const color =
|
||||
value === "added"
|
||||
? "is-success"
|
||||
? "success is-outlined"
|
||||
: value === "deleted"
|
||||
? "is-danger"
|
||||
: "is-info";
|
||||
? "danger is-outlined"
|
||||
: "info is-outlined";
|
||||
|
||||
return (
|
||||
<span
|
||||
<Tag
|
||||
className={classNames(
|
||||
"tag",
|
||||
"is-rounded",
|
||||
"has-text-weight-normal",
|
||||
color,
|
||||
classes.changeType
|
||||
)}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
color={color}
|
||||
label={value}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -219,9 +248,12 @@ class DiffFile extends React.Component<Props, State> {
|
||||
: null;
|
||||
icon = "fa fa-angle-down";
|
||||
body = (
|
||||
<div className="panel-block is-paddingless is-size-7">
|
||||
<div className="panel-block is-paddingless">
|
||||
{fileAnnotations}
|
||||
<DiffComponent viewType={viewType}>
|
||||
<DiffComponent
|
||||
className={classNames(viewType, classes.diff)}
|
||||
viewType={viewType}
|
||||
>
|
||||
{file.hunks.map(this.renderHunk)}
|
||||
</DiffComponent>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ const styles = {
|
||||
}
|
||||
},
|
||||
avatarFigure: {
|
||||
marginTop: ".25rem",
|
||||
marginTop: ".5rem",
|
||||
marginRight: ".5rem"
|
||||
},
|
||||
avatarImage: {
|
||||
@@ -35,6 +35,9 @@ const styles = {
|
||||
metadata: {
|
||||
marginLeft: 0
|
||||
},
|
||||
authorMargin: {
|
||||
marginTop: "0.5rem"
|
||||
},
|
||||
isVcentered: {
|
||||
alignSelf: "center"
|
||||
},
|
||||
@@ -70,15 +73,6 @@ class ChangesetRow extends React.Component<Props> {
|
||||
<div className="column is-three-fifths">
|
||||
<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
|
||||
@@ -90,6 +84,15 @@ class ChangesetRow extends React.Component<Props> {
|
||||
</figure>
|
||||
</AvatarWrapper>
|
||||
<div className={classNames(classes.metadata, "media-right")}>
|
||||
<h4 className="has-text-weight-bold is-ellipsis-overflow">
|
||||
<ExtensionPoint
|
||||
name="changeset.description"
|
||||
props={{ changeset, value: description.title }}
|
||||
renderAll={false}
|
||||
>
|
||||
{description.title}
|
||||
</ExtensionPoint>
|
||||
</h4>
|
||||
<p className="is-hidden-touch">
|
||||
<Interpolate
|
||||
i18nKey="changeset.summary"
|
||||
@@ -104,7 +107,7 @@ class ChangesetRow extends React.Component<Props> {
|
||||
time={dateFromNow}
|
||||
/>
|
||||
</p>
|
||||
<p className="is-size-7">
|
||||
<p className={classNames("is-size-7", classes.authorMargin)}>
|
||||
<ChangesetAuthor changeset={changeset} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ type Props = {
|
||||
class ChangesetTag extends React.Component<Props> {
|
||||
render() {
|
||||
const { tag } = this.props;
|
||||
return <ChangesetTagBase icon={"fa-tag"} label={tag.name} />;
|
||||
return <ChangesetTagBase icon="tag" label={tag.name} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
|
||||
const styles = {
|
||||
spacing: {
|
||||
marginRight: ".25rem"
|
||||
}
|
||||
};
|
||||
import Tag from "../../Tag";
|
||||
|
||||
type Props = {
|
||||
icon: string,
|
||||
label: string,
|
||||
|
||||
// context props
|
||||
classes: Object
|
||||
label: string
|
||||
};
|
||||
|
||||
class ChangesetTagBase extends React.Component<Props> {
|
||||
render() {
|
||||
const { icon, label, classes } = this.props;
|
||||
const { icon, label } = this.props;
|
||||
return (
|
||||
<span className={classNames("tag", "is-info")}>
|
||||
<span className={classNames("fa", icon, classes.spacing)} /> {label}
|
||||
</span>
|
||||
<Tag color="info" icon={icon} label={label} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(ChangesetTagBase);
|
||||
export default ChangesetTagBase;
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Tag } from "@scm-manager/ui-types";
|
||||
import ChangesetTagBase from "./ChangesetTagBase";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Tag } from "@scm-manager/ui-types";
|
||||
import Tooltip from "../../Tooltip";
|
||||
import ChangesetTagBase from "./ChangesetTagBase";
|
||||
|
||||
type Props = {
|
||||
tags: Tag[],
|
||||
|
||||
// context props
|
||||
t: (string) => string
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class ChangesetTagsCollapsed extends React.Component<Props> {
|
||||
render() {
|
||||
const { tags, t } = this.props;
|
||||
const message = tags.map((tag) => tag.name).join(", ");
|
||||
const message = tags.map(tag => tag.name).join(", ");
|
||||
return (
|
||||
<Tooltip location="top" message={message}>
|
||||
<ChangesetTagBase icon={"fa-tags"} label={ tags.length + " " + t("changeset.tags") } />
|
||||
<ChangesetTagBase
|
||||
icon="tags"
|
||||
label={tags.length + " " + t("changeset.tags")}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
//@flow
|
||||
import type { Links } from "./hal";
|
||||
|
||||
export type DisplayedUser = {
|
||||
id: string,
|
||||
displayName: string,
|
||||
mail: string
|
||||
};
|
||||
|
||||
export type User = {
|
||||
displayName: string,
|
||||
name: string,
|
||||
|
||||
@@ -3,7 +3,7 @@ export type { Action } from "./Action";
|
||||
export type { Link, Links, Collection, PagedCollection } from "./hal";
|
||||
|
||||
export type { Me } from "./Me";
|
||||
export type { User } from "./User";
|
||||
export type { DisplayedUser, User } from "./User";
|
||||
export type { Group, Member } from "./Group";
|
||||
|
||||
export type { Repository, RepositoryCollection, RepositoryGroup } from "./Repositories";
|
||||
|
||||
@@ -79,8 +79,6 @@
|
||||
"password": {
|
||||
"label": "Passwort",
|
||||
"newPassword": "Neues Passwort",
|
||||
"passwordHelpText": "Klartext Passwort des Benutzers.",
|
||||
"passwordConfirmHelpText": "Passwort zur Bestätigen wiederholen.",
|
||||
"currentPassword": "Aktuelles Passwort",
|
||||
"currentPasswordHelpText": "Dieses Passwort wird momentan bereits verwendet.",
|
||||
"confirmPassword": "Passwort wiederholen",
|
||||
|
||||
@@ -38,15 +38,11 @@
|
||||
"add-member-button": {
|
||||
"label": "Mitglied hinzufügen"
|
||||
},
|
||||
"remove-member-button": {
|
||||
"label": "Mitglied entfernen"
|
||||
},
|
||||
"add-member-textfield": {
|
||||
"label": "Mitglied hinzufügen",
|
||||
"error": "Ungültiger Name für Mitglied"
|
||||
},
|
||||
"add-member-autocomplete": {
|
||||
"placeholder": "Benutzername eingeben",
|
||||
"placeholder": "Mitglied hinzufügen",
|
||||
"loading": "Suche...",
|
||||
"no-options": "Kein Vorschlag für Benutzername verfügbar"
|
||||
},
|
||||
|
||||
@@ -80,8 +80,6 @@
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"newPassword": "New password",
|
||||
"passwordHelpText": "Plain text password of the user",
|
||||
"passwordConfirmHelpText": "Repeat the password for confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordHelpText": "The password currently in use",
|
||||
"confirmPassword": "Confirm password",
|
||||
|
||||
@@ -38,15 +38,11 @@
|
||||
"add-member-button": {
|
||||
"label": "Add Member"
|
||||
},
|
||||
"remove-member-button": {
|
||||
"label": "Remove Member"
|
||||
},
|
||||
"add-member-textfield": {
|
||||
"label": "Add Member",
|
||||
"error": "Invalid member name"
|
||||
},
|
||||
"add-member-autocomplete": {
|
||||
"placeholder": "Enter Member",
|
||||
"placeholder": "Add Member",
|
||||
"loading": "Loading...",
|
||||
"no-options": "No suggestion available"
|
||||
},
|
||||
|
||||
@@ -38,15 +38,11 @@
|
||||
"add-member-button": {
|
||||
"label": "Añadir miembro"
|
||||
},
|
||||
"remove-member-button": {
|
||||
"label": "Eliminar miembro"
|
||||
},
|
||||
"add-member-textfield": {
|
||||
"label": "Añadir miembro",
|
||||
"error": "El nombre del miembro es incorrecto"
|
||||
},
|
||||
"add-member-autocomplete": {
|
||||
"placeholder": "Introducir el nombre del miembro",
|
||||
"placeholder": "Añadir miembro",
|
||||
"loading": "Cargando...",
|
||||
"no-options": "No hay sugerencias disponibles"
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import { translate } from "react-i18next";
|
||||
import { Tag } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
system?: boolean,
|
||||
@@ -25,9 +25,11 @@ class SystemRoleTag extends React.Component<Props> {
|
||||
|
||||
if (system) {
|
||||
return (
|
||||
<span className={classNames("tag is-dark", classes.tag)}>
|
||||
{t("repositoryRole.system")}
|
||||
</span>
|
||||
<Tag
|
||||
className={classes.tag}
|
||||
color="dark"
|
||||
label={t("repositoryRole.system")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -21,7 +21,8 @@ const styles = {
|
||||
marginTop: "0.5em"
|
||||
},
|
||||
content: {
|
||||
marginLeft: "1.5em"
|
||||
marginLeft: "1.5em",
|
||||
minHeight: "10.5rem"
|
||||
},
|
||||
link: {
|
||||
display: "block",
|
||||
|
||||
@@ -110,6 +110,8 @@ class ChangeUserPassword extends React.Component<Props, State> {
|
||||
return (
|
||||
<form onSubmit={this.submit}>
|
||||
{message}
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<InputField
|
||||
label={t("password.currentPassword")}
|
||||
type="password"
|
||||
@@ -119,21 +121,31 @@ class ChangeUserPassword extends React.Component<Props, State> {
|
||||
value={this.state.oldPassword ? this.state.oldPassword : ""}
|
||||
helpText={t("password.currentPasswordHelpText")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<PasswordConfirmation
|
||||
passwordChanged={this.passwordChanged}
|
||||
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
||||
/>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<SubmitButton
|
||||
disabled={!this.isValid()}
|
||||
loading={loading}
|
||||
label={t("password.submit")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
passwordChanged = (password: string, passwordValid: boolean) => {
|
||||
this.setState({ ...this.state, password, passwordValid: (!!password && passwordValid) });
|
||||
this.setState({
|
||||
...this.state,
|
||||
password,
|
||||
passwordValid: !!password && passwordValid
|
||||
});
|
||||
};
|
||||
|
||||
onClose = () => {
|
||||
|
||||
@@ -4,8 +4,7 @@ import { translate } from "react-i18next";
|
||||
import {
|
||||
Subtitle,
|
||||
AutocompleteAddEntryToTableField,
|
||||
LabelWithHelpIcon,
|
||||
MemberNameTable,
|
||||
MemberNameTagGroup,
|
||||
InputField,
|
||||
SubmitButton,
|
||||
Textarea,
|
||||
@@ -55,10 +54,7 @@ class GroupForm extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
isFalsy(value) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return !value;
|
||||
}
|
||||
|
||||
isValid = () => {
|
||||
@@ -85,11 +81,7 @@ class GroupForm extends React.Component<Props, State> {
|
||||
const { loadUserSuggestions, t } = this.props;
|
||||
return (
|
||||
<>
|
||||
<LabelWithHelpIcon
|
||||
label={t("group.members")}
|
||||
helpText={t("groupForm.help.memberHelpText")}
|
||||
/>
|
||||
<MemberNameTable
|
||||
<MemberNameTagGroup
|
||||
members={group.members}
|
||||
memberListChanged={this.memberListChanged}
|
||||
/>
|
||||
@@ -97,7 +89,6 @@ class GroupForm extends React.Component<Props, State> {
|
||||
addEntry={this.addMember}
|
||||
disabled={false}
|
||||
buttonLabel={t("add-member-button.label")}
|
||||
fieldLabel={t("add-member-textfield.label")}
|
||||
errorMessage={t("add-member-textfield.error")}
|
||||
loadSuggestions={loadUserSuggestions}
|
||||
placeholder={t("add-member-autocomplete.placeholder")}
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import type { Link } from "@scm-manager/ui-types";
|
||||
import {
|
||||
Notification,
|
||||
ErrorNotification,
|
||||
SubmitButton
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import { getLink } from "../../modules/indexResource";
|
||||
import {
|
||||
loadPermissionsForEntity,
|
||||
setPermissions
|
||||
} from "./handlePermissions";
|
||||
import PermissionCheckbox from "./PermissionCheckbox";
|
||||
import { connect } from "react-redux";
|
||||
import { getLink } from "../../modules/indexResource";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
availablePermissionLink: string,
|
||||
selectedPermissionsLink: Link
|
||||
selectedPermissionsLink: Link,
|
||||
|
||||
// context props
|
||||
classes: any,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -30,6 +35,17 @@ type State = {
|
||||
overwritePermissionsLink?: Link
|
||||
};
|
||||
|
||||
const styles = {
|
||||
permissionsWrapper: {
|
||||
paddingBottom: "0",
|
||||
|
||||
"& .field .control": {
|
||||
width: "100%",
|
||||
wordWrap: "break-word"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SetPermissions extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -135,17 +151,35 @@ class SetPermissions extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
renderPermissions = () => {
|
||||
const { classes } = this.props;
|
||||
const { overwritePermissionsLink, permissions } = this.state;
|
||||
return Object.keys(permissions).map(p => (
|
||||
<div key={p}>
|
||||
const permissionArray = Object.keys(permissions);
|
||||
return (
|
||||
<div className="columns">
|
||||
<div className={classNames("column", "is-half", classes.permissionsWrapper)}>
|
||||
{permissionArray.slice(0, (permissionArray.length/2)+1).map(p => (
|
||||
<PermissionCheckbox
|
||||
key={p}
|
||||
permission={p}
|
||||
checked={permissions[p]}
|
||||
onChange={this.valueChanged}
|
||||
disabled={!overwritePermissionsLink}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
));
|
||||
<div className={classNames("column", "is-half", classes.permissionsWrapper)}>
|
||||
{permissionArray.slice((permissionArray.length/2)+1, permissionArray.length).map(p => (
|
||||
<PermissionCheckbox
|
||||
key={p}
|
||||
permission={p}
|
||||
checked={permissions[p]}
|
||||
onChange={this.valueChanged}
|
||||
disabled={!overwritePermissionsLink}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
valueChanged = (value: boolean, name: string) => {
|
||||
@@ -174,5 +208,4 @@ const mapStateToProps = state => {
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(
|
||||
translate("permissions")(SetPermissions)
|
||||
);
|
||||
injectSheet(styles)(translate("permissions")(SetPermissions)));
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import { translate } from "react-i18next";
|
||||
import { Tag } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
defaultBranch?: boolean,
|
||||
|
||||
// context props
|
||||
classes: any,
|
||||
t: string => string
|
||||
};
|
||||
@@ -23,9 +25,11 @@ class DefaultBranchTag extends React.Component<Props> {
|
||||
|
||||
if (defaultBranch) {
|
||||
return (
|
||||
<span className={classNames("tag is-dark", classes.tag)}>
|
||||
{t("branch.defaultTag")}
|
||||
</span>
|
||||
<Tag
|
||||
className={classes.tag}
|
||||
color="dark"
|
||||
label={t("branch.defaultTag")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -105,17 +105,25 @@ class SetUserPassword extends React.Component<Props, State> {
|
||||
passwordChanged={this.passwordChanged}
|
||||
key={this.state.passwordChanged ? "changed" : "unchanged"}
|
||||
/>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<SubmitButton
|
||||
disabled={!this.state.passwordValid}
|
||||
loading={loading}
|
||||
label={t("singleUserPassword.button")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
passwordChanged = (password: string, passwordValid: boolean) => {
|
||||
this.setState({ ...this.state, password, passwordValid: (!!password && passwordValid) });
|
||||
this.setState({
|
||||
...this.state,
|
||||
password,
|
||||
passwordValid: !!password && passwordValid
|
||||
});
|
||||
};
|
||||
|
||||
onClose = () => {
|
||||
|
||||
@@ -162,9 +162,9 @@ class UserForm extends React.Component<Props, State> {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{passwordChangeField}
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
{passwordChangeField}
|
||||
<Checkbox
|
||||
label={t("user.active")}
|
||||
onChange={this.handleActiveChange}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@import "bulma/sass/utilities/initial-variables";
|
||||
@import "bulma/sass/utilities/functions";
|
||||
|
||||
$turquoise: #00d1df;
|
||||
$blue: #33b2e8;
|
||||
$cyan: $blue;
|
||||
$green: #00c79b;
|
||||
$mint: #11dfd0;
|
||||
|
||||
.is-ellipsis-overflow {
|
||||
overflow: hidden;
|
||||
@@ -36,7 +36,7 @@ $mint: #11dfd0;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: calc(100vh - 260px);
|
||||
min-height: calc(100vh - 300px);
|
||||
}
|
||||
|
||||
// shown in top section when pageactions set
|
||||
@@ -96,7 +96,14 @@ $danger-75: scale-color($danger, $lightness: 25%);
|
||||
$danger-50: scale-color($danger, $lightness: 50%);
|
||||
$danger-25: scale-color($danger, $lightness: 75%);
|
||||
|
||||
/*
|
||||
// not supported by ie
|
||||
// css vars for external reuse
|
||||
:root {
|
||||
// asc sorted initial variables
|
||||
--black: #{$black};
|
||||
--white: #{$white};
|
||||
|
||||
// asc sorted derived-variables
|
||||
--primary: #{$primary};
|
||||
--primary-75: #{$primary-75};
|
||||
@@ -131,7 +138,14 @@ $danger-25: scale-color($danger, $lightness: 75%);
|
||||
--link-50: #{$link-50};
|
||||
--link-25: #{$link-25};
|
||||
}
|
||||
*/
|
||||
|
||||
// readability issues with original color
|
||||
.has-text-warning {
|
||||
color: #ffb600 !important;
|
||||
}
|
||||
|
||||
// border and background colors
|
||||
.has-background-dark-75 {
|
||||
background-color: $dark-75;
|
||||
}
|
||||
@@ -196,11 +210,183 @@ $danger-25: scale-color($danger, $lightness: 75%);
|
||||
background-color: $danger-25;
|
||||
}
|
||||
|
||||
.has-background-brown {
|
||||
background-color: #000000b3;
|
||||
.has-background-warning-invert {
|
||||
background-color: $warning-invert;
|
||||
}
|
||||
|
||||
// tags
|
||||
.tag:not(body) {
|
||||
border: 1px solid transparent;
|
||||
background-color: $white;
|
||||
|
||||
&.is-delete {
|
||||
background-color: $light;
|
||||
}
|
||||
|
||||
&.is-outlined {
|
||||
background-color: $white;
|
||||
}
|
||||
&.is-black.is-outlined {
|
||||
color: $black;
|
||||
border-color: $black;
|
||||
}
|
||||
&.is-dark.is-outlined {
|
||||
color: $dark;
|
||||
border-color: $dark;
|
||||
}
|
||||
&.is-light.is-outlined {
|
||||
color: $light;
|
||||
border-color: $light;
|
||||
}
|
||||
&.is-white.is-outlined {
|
||||
color: $white;
|
||||
border-color: $white;
|
||||
}
|
||||
&.is-primary.is-outlined {
|
||||
color: $primary;
|
||||
border-color: $primary;
|
||||
}
|
||||
&.is-link.is-outlined {
|
||||
color: $link;
|
||||
border-color: $link;
|
||||
}
|
||||
&.is-info.is-outlined {
|
||||
color: $info;
|
||||
border-color: $info;
|
||||
}
|
||||
&.is-success.is-outlined {
|
||||
color: $success;
|
||||
border-color: $success;
|
||||
}
|
||||
&.is-warning.is-outlined {
|
||||
color: $warning;
|
||||
border-color: $warning;
|
||||
}
|
||||
&.is-danger.is-outlined {
|
||||
color: $danger;
|
||||
border-color: $danger;
|
||||
}
|
||||
}
|
||||
|
||||
// buttons
|
||||
.button {
|
||||
padding-left: 1.5em;
|
||||
padding-right: 1.5em;
|
||||
height: 2.5rem;
|
||||
font-weight: 600;
|
||||
|
||||
&[disabled] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.is-primary:hover,
|
||||
&.is-primary.is-hovered {
|
||||
background-color: scale-color($primary, $lightness: -10%);
|
||||
}
|
||||
|
||||
&.is-primary:active,
|
||||
&.is-primary.is-active {
|
||||
background-color: scale-color($primary, $lightness: -20%);
|
||||
}
|
||||
|
||||
&.is-primary[disabled] {
|
||||
background-color: scale-color($primary, $lightness: 75%);
|
||||
}
|
||||
|
||||
&.is-info:hover,
|
||||
&.is-info.is-hovered {
|
||||
background-color: scale-color($info, $lightness: -10%);
|
||||
}
|
||||
|
||||
&.is-info:active,
|
||||
&.is-info.is-active {
|
||||
background-color: scale-color($info, $lightness: -20%);
|
||||
}
|
||||
|
||||
&.is-info[disabled] {
|
||||
background-color: scale-color($info, $lightness: 75%);
|
||||
}
|
||||
|
||||
&.is-link:hover,
|
||||
&.is-link.is-hovered {
|
||||
background-color: scale-color($link, $lightness: -10%);
|
||||
}
|
||||
|
||||
&.is-link:active,
|
||||
&.is-link.is-active {
|
||||
background-color: scale-color($link, $lightness: -20%);
|
||||
}
|
||||
|
||||
&.is-link[disabled] {
|
||||
background-color: scale-color($link, $lightness: 75%);
|
||||
}
|
||||
|
||||
&.is-success:hover,
|
||||
&.is-success.is-hovered {
|
||||
background-color: scale-color($success, $lightness: -10%);
|
||||
}
|
||||
|
||||
&.is-success:active,
|
||||
&.is-success.is-active {
|
||||
background-color: scale-color($success, $lightness: -20%);
|
||||
}
|
||||
|
||||
&.is-success[disabled] {
|
||||
background-color: scale-color($success, $lightness: 75%);
|
||||
}
|
||||
|
||||
&.is-warning:hover,
|
||||
&.is-warning.is-hovered {
|
||||
background-color: scale-color($warning, $lightness: -10%);
|
||||
}
|
||||
|
||||
&.is-warning:active,
|
||||
&.is-warning.is-active {
|
||||
background-color: scale-color($warning, $lightness: -20%);
|
||||
}
|
||||
|
||||
&.is-warning[disabled] {
|
||||
background-color: scale-color($warning, $lightness: 75%);
|
||||
}
|
||||
|
||||
&.is-danger:hover,
|
||||
&.is-danger.is-hovered {
|
||||
background-color: scale-color($danger, $lightness: -10%);
|
||||
}
|
||||
|
||||
&.is-danger:active,
|
||||
&.is-danger.is-active {
|
||||
background-color: scale-color($danger, $lightness: -20%);
|
||||
}
|
||||
|
||||
&.is-danger[disabled] {
|
||||
background-color: scale-color($danger, $lightness: 75%);
|
||||
}
|
||||
|
||||
&.is-reduced-mobile,
|
||||
&.reduced-mobile {
|
||||
font-size: 0.9rem;
|
||||
|
||||
@media screen and (max-width: 1087px) {
|
||||
> span:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1087px) and (min-width: 769px) {
|
||||
// simultaneously with left margin of Bulma
|
||||
> .icon:first-child:not(:last-child) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
// simultaneously with left margin of Bulma
|
||||
.icon:first-child:not(:last-child) {
|
||||
margin-right: calc(-0.375em - 1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// import at the end, because we need a lot of stuff from bulma/bulma
|
||||
.box-link-shadow {
|
||||
&:hover,
|
||||
&:focus {
|
||||
@@ -235,53 +421,7 @@ ul.is-separated {
|
||||
}
|
||||
}
|
||||
|
||||
.user {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// buttons
|
||||
.button {
|
||||
padding-left: 1.5em;
|
||||
padding-right: 1.5em;
|
||||
height: 2.5rem;
|
||||
|
||||
&.is-primary {
|
||||
background-color: #00d1df;
|
||||
}
|
||||
&.is-primary:hover,
|
||||
&.is-primary.is-hovered {
|
||||
background-color: #00b9c6;
|
||||
}
|
||||
&.is-primary:active,
|
||||
&.is-primary.is-active {
|
||||
background-color: #00a1ac;
|
||||
}
|
||||
&.is-primary[disabled] {
|
||||
background-color: #40dde7;
|
||||
}
|
||||
&.reduced-mobile {
|
||||
@media screen and (max-width: 1087px) {
|
||||
> span:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1087px) and (min-width: 769px) {
|
||||
// simultaneously with left margin of Bulma
|
||||
> .icon:first-child:not(:last-child) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
// simultaneously with left margin of Bulma
|
||||
.icon:first-child:not(:last-child) {
|
||||
margin-right: calc(-0.375em - 1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multiline Columns
|
||||
// columns
|
||||
.columns.is-multiline {
|
||||
.column {
|
||||
height: 120px;
|
||||
@@ -416,7 +556,7 @@ ul.is-separated {
|
||||
|
||||
.panel-heading {
|
||||
border: none;
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
border-bottom: 1px solid $border;
|
||||
border-radius: 0.25rem 0.25rem 0 0;
|
||||
|
||||
> .field {
|
||||
@@ -427,14 +567,6 @@ ul.is-separated {
|
||||
.panel-block {
|
||||
display: block;
|
||||
border: none;
|
||||
|
||||
& .comment-wrapper:first-child div:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
& .diff-widget-content div {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-footer {
|
||||
@@ -445,7 +577,7 @@ ul.is-separated {
|
||||
line-height: 1.25;
|
||||
padding: 0.5em 0.75em;
|
||||
border: none;
|
||||
border-top: 1px solid #dbdbdb;
|
||||
border-top: 1px solid $border;
|
||||
border-radius: 0 0 0.25rem 0.25rem;
|
||||
}
|
||||
}
|
||||
@@ -470,10 +602,6 @@ form .field:not(.is-grouped) {
|
||||
}
|
||||
}
|
||||
|
||||
.is-icon {
|
||||
color: $grey-light;
|
||||
}
|
||||
|
||||
// label with help-icon compensation
|
||||
.label-icon-spacing {
|
||||
margin-top: 30px;
|
||||
@@ -499,14 +627,19 @@ form .field:not(.is-grouped) {
|
||||
.pagination-ellipsis {
|
||||
padding-left: 1.5em;
|
||||
padding-right: 1.5em;
|
||||
height: 2.5rem;
|
||||
height: 2.8rem;
|
||||
min-width: 5rem;
|
||||
}
|
||||
.pagination-previous,
|
||||
.pagination-next {
|
||||
height: 2.8rem;
|
||||
min-width: 6.75em;
|
||||
}
|
||||
.pagination-link.is-current {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// dark hero colors
|
||||
// hero
|
||||
.hero.is-dark {
|
||||
background-color: #002e4b;
|
||||
background-image: url(../images/scmManagerHero.jpg);
|
||||
@@ -528,7 +661,7 @@ form .field:not(.is-grouped) {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
// sidebar menu
|
||||
// aside
|
||||
.aside-background {
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
@@ -537,6 +670,8 @@ form .field:not(.is-grouped) {
|
||||
top: 0;
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
// menu
|
||||
.menu {
|
||||
div {
|
||||
height: 100%;
|
||||
@@ -544,7 +679,6 @@ form .field:not(.is-grouped) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
color: #fff;
|
||||
font-size: 1em;
|
||||
@@ -614,17 +748,6 @@ form .field:not(.is-grouped) {
|
||||
}
|
||||
}
|
||||
|
||||
// modal
|
||||
.modal {
|
||||
.modal-card-foot {
|
||||
justify-content: flex-end; // pulled-right
|
||||
}
|
||||
}
|
||||
|
||||
.modal-card-body div div:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.sub-menu li {
|
||||
line-height: 1;
|
||||
|
||||
@@ -650,6 +773,17 @@ form .field:not(.is-grouped) {
|
||||
}
|
||||
}
|
||||
|
||||
// modal
|
||||
.modal {
|
||||
.modal-card-foot {
|
||||
justify-content: flex-end; // pulled-right
|
||||
}
|
||||
}
|
||||
|
||||
.modal-card-body div div:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
// cursor
|
||||
.has-cursor-pointer {
|
||||
cursor: pointer;
|
||||
|
||||
Reference in New Issue
Block a user