mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 03:25:56 +01:00
fixed branch selection urls and styles branch selector
This commit is contained in:
@@ -3,16 +3,16 @@ import React from "react";
|
|||||||
import {translate} from "react-i18next";
|
import {translate} from "react-i18next";
|
||||||
import type {PagedCollection} from "@scm-manager/ui-types";
|
import type {PagedCollection} from "@scm-manager/ui-types";
|
||||||
import {Button} from "./buttons";
|
import {Button} from "./buttons";
|
||||||
import {withRouter} from "react-router-dom";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
collection: PagedCollection,
|
collection: PagedCollection,
|
||||||
t: string => string,
|
page: number,
|
||||||
match: any
|
|
||||||
|
// context props
|
||||||
|
t: string => string
|
||||||
};
|
};
|
||||||
|
|
||||||
class LinkPaginator extends React.Component<Props> {
|
class LinkPaginator extends React.Component<Props> {
|
||||||
//TODO: HATEOAS-Links verwenden
|
|
||||||
|
|
||||||
renderFirstButton() {
|
renderFirstButton() {
|
||||||
return (
|
return (
|
||||||
@@ -26,30 +26,32 @@ class LinkPaginator extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderPreviousButton(label?: string) {
|
renderPreviousButton(label?: string) {
|
||||||
const { match } = this.props;
|
const { page } = this.props;
|
||||||
const page = parseInt(match.params.page) || 1;
|
|
||||||
const previousPage = page - 1;
|
const previousPage = page - 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className={"pagination-previous"}
|
className={"pagination-previous"}
|
||||||
label={label ? label : previousPage.toString()}
|
label={label ? label : previousPage.toString()}
|
||||||
disabled={previousPage < 1}
|
disabled={!this.hasLink("prev")}
|
||||||
link={`${previousPage}`}
|
link={`${previousPage}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNextButton(label?: string) {
|
hasLink(name: string) {
|
||||||
const { match, collection } = this.props;
|
const { collection } = this.props;
|
||||||
let page = parseInt(match.params.page) || 1;
|
return collection._links[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNextButton(label?: string) {
|
||||||
|
const { page } = this.props;
|
||||||
const nextPage = page + 1;
|
const nextPage = page + 1;
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className={"pagination-next"}
|
className={"pagination-next"}
|
||||||
label={label ? label : nextPage.toString()}
|
label={label ? label : nextPage.toString()}
|
||||||
disabled={nextPage >= collection.pageTotal + 1}
|
disabled={!this.hasLink("next")}
|
||||||
link={`${nextPage}`}
|
link={`${nextPage}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -128,4 +130,4 @@ class LinkPaginator extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withRouter(translate("commons")(LinkPaginator));
|
export default translate("commons")(LinkPaginator);
|
||||||
|
|||||||
@@ -59,6 +59,9 @@
|
|||||||
"mail": "Mail"
|
"mail": "Mail"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"branch-selector": {
|
||||||
|
"label": "Branches"
|
||||||
|
},
|
||||||
"branch-chooser": {
|
"branch-chooser": {
|
||||||
"error-title": "Error",
|
"error-title": "Error",
|
||||||
"error-subtitle": "Could not fetch branches"
|
"error-subtitle": "Could not fetch branches"
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
options: string[],
|
options: string[],
|
||||||
optionSelected: string => void,
|
optionSelected: string => void,
|
||||||
preselectedOption?: string
|
preselectedOption?: string,
|
||||||
|
className: any
|
||||||
};
|
};
|
||||||
|
|
||||||
class DropDown extends React.Component<Props> {
|
class DropDown extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { options, preselectedOption } = this.props;
|
const { options, preselectedOption, className } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="select">
|
<div className={classNames(className, "select")}>
|
||||||
<select
|
<select
|
||||||
value={preselectedOption ? preselectedOption : ""}
|
value={preselectedOption ? preselectedOption : ""}
|
||||||
onChange={this.change}
|
onChange={this.change}
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
// @flow
|
|
||||||
import * as React from "react";
|
|
||||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import {
|
|
||||||
fetchBranches,
|
|
||||||
getBranches,
|
|
||||||
getFetchBranchesFailure,
|
|
||||||
isFetchBranchesPending
|
|
||||||
} from "../modules/branches";
|
|
||||||
import DropDown from "../components/DropDown";
|
|
||||||
import type { History } from "history";
|
|
||||||
import { ErrorPage, Loading } from "@scm-manager/ui-components";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
repository: Repository,
|
|
||||||
label: string, //TODO: Should this be here?
|
|
||||||
onChange: string => void,
|
|
||||||
children: React.Node,
|
|
||||||
selected?: string,
|
|
||||||
|
|
||||||
// State props
|
|
||||||
branches: Branch[],
|
|
||||||
error: Error,
|
|
||||||
loading: boolean,
|
|
||||||
|
|
||||||
// Dispatch props
|
|
||||||
fetchBranches: Repository => void,
|
|
||||||
|
|
||||||
// Context props
|
|
||||||
t: string => string
|
|
||||||
};
|
|
||||||
type State = {
|
|
||||||
selectedBranch?: Branch
|
|
||||||
};
|
|
||||||
|
|
||||||
class BranchChooser extends React.Component<Props, State> {
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchBranches(this.props.repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const { loading, error, t, repository } = this.props;
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<ErrorPage
|
|
||||||
title={t("branch-chooser.error-title")}
|
|
||||||
subtitle={t("branch-chooser.error-subtitle")}
|
|
||||||
error={error}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!repository) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <Loading />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { selectedBranch } = this.state;
|
|
||||||
|
|
||||||
const childrenWithBranch = React.Children.map(
|
|
||||||
this.props.children,
|
|
||||||
child => {
|
|
||||||
return React.cloneElement(child, {
|
|
||||||
branch: selectedBranch
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{this.renderBranchChooser()}
|
|
||||||
{childrenWithBranch}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBranchChooser() {
|
|
||||||
const { label, branches, selected } = this.props;
|
|
||||||
|
|
||||||
if (!branches || branches.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"box"}>
|
|
||||||
<label className="label">{label}</label>
|
|
||||||
<DropDown
|
|
||||||
options={branches.map(b => b.name)}
|
|
||||||
preselectedOption={
|
|
||||||
this.state.selectedBranch
|
|
||||||
? this.state.selectedBranch.name
|
|
||||||
: selected
|
|
||||||
}
|
|
||||||
optionSelected={(branchName: string) => {
|
|
||||||
this.branchSelected(branchName, true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
branchSelected = (branch: string, changed: boolean) => {
|
|
||||||
for (let b of this.props.branches) {
|
|
||||||
if (b.name === branch) {
|
|
||||||
this.updateBranch(branch, b, changed);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
updateBranch = (branchName: string, branch: Branch, changed: boolean) => {
|
|
||||||
this.setState(
|
|
||||||
prevState => {
|
|
||||||
if (
|
|
||||||
!prevState.selectedBranch ||
|
|
||||||
branchName !== prevState.selectedBranch.name
|
|
||||||
) {
|
|
||||||
return { selectedBranch: branch };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
if (changed) {
|
|
||||||
this.props.onChange(branch.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
|
||||||
return {
|
|
||||||
fetchBranches: (repo: Repository) => {
|
|
||||||
dispatch(fetchBranches(repo));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
|
||||||
const { repository } = ownProps;
|
|
||||||
const loading = isFetchBranchesPending(state, repository);
|
|
||||||
const error = getFetchBranchesFailure(state, repository);
|
|
||||||
|
|
||||||
const branches = getBranches(state, repository);
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
error,
|
|
||||||
branches
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(translate("repos")(BranchChooser));
|
|
||||||
@@ -19,6 +19,8 @@ type Props = {
|
|||||||
repository: Repository,
|
repository: Repository,
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
selected: string,
|
selected: string,
|
||||||
|
baseUrlWithBranch: string,
|
||||||
|
baseUrlWithoutBranch: string,
|
||||||
|
|
||||||
// State props
|
// State props
|
||||||
branches: Branch[],
|
branches: Branch[],
|
||||||
@@ -49,15 +51,16 @@ class BranchRoot extends React.Component<Props> {
|
|||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
matchedUrl = () => {
|
branchSelected = (branch?: Branch) => {
|
||||||
return this.stripEndingSlash(this.props.baseUrl);
|
let url;
|
||||||
};
|
if (branch) {
|
||||||
|
url = `${this.props.baseUrlWithBranch}/${encodeURIComponent(
|
||||||
branchSelected = (branch: Branch) => {
|
branch.name
|
||||||
const url = this.matchedUrl();
|
)}/changesets/`;
|
||||||
this.props.history.push(
|
} else {
|
||||||
`${url}/${encodeURIComponent(branch.name)}/changesets/`
|
url = `${this.props.baseUrlWithoutBranch}/`;
|
||||||
);
|
}
|
||||||
|
this.props.history.push(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
findSelectedBranch = () => {
|
findSelectedBranch = () => {
|
||||||
|
|||||||
@@ -3,10 +3,24 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Branch } from "@scm-manager/ui-types";
|
import type { Branch } from "@scm-manager/ui-types";
|
||||||
import DropDown from "../components/DropDown";
|
import DropDown from "../components/DropDown";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import injectSheet from "react-jss";
|
||||||
|
import { compose } from "redux";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
zeroflex: {
|
||||||
|
flexGrow: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
branches: Branch[], // TODO: Use generics?
|
branches: Branch[], // TODO: Use generics?
|
||||||
selected?: Branch => void
|
selected?: Branch => void,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
classes: Object,
|
||||||
|
t: string => string
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = { selectedBranch?: Branch };
|
type State = { selectedBranch?: Branch };
|
||||||
@@ -18,19 +32,33 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { branches } = this.props;
|
const { branches, classes, t } = this.props;
|
||||||
|
|
||||||
if (branches) {
|
if (branches) {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="box field is-horizontal">
|
||||||
<DropDown
|
<div
|
||||||
options={branches.map(b => b.name)}
|
className={classNames("field-label", "is-normal", classes.zeroflex)}
|
||||||
optionSelected={this.branchSelected}
|
>
|
||||||
preselectedOption={
|
<label className="label">{t("branch-selector.label")}</label>
|
||||||
this.state.selectedBranch ? this.state.selectedBranch.name : ""
|
</div>
|
||||||
}
|
<div className="field-body">
|
||||||
/>
|
<div className="field is-narrow">
|
||||||
</>
|
<div className="control">
|
||||||
|
<DropDown
|
||||||
|
className="is-fullwidth"
|
||||||
|
options={branches.map(b => b.name)}
|
||||||
|
optionSelected={this.branchSelected}
|
||||||
|
preselectedOption={
|
||||||
|
this.state.selectedBranch
|
||||||
|
? this.state.selectedBranch.name
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,11 +67,12 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
const { branches, selected } = this.props;
|
const { branches, selected } = this.props;
|
||||||
const branch = branches.find(b => b.name === branchName);
|
const branch = branches.find(b => b.name === branchName);
|
||||||
|
|
||||||
if (branch) {
|
selected(branch);
|
||||||
selected(branch);
|
this.setState({ selectedBranch: branch });
|
||||||
this.setState({ selectedBranch: branch });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BranchSelector;
|
export default compose(
|
||||||
|
injectSheet(styles),
|
||||||
|
translate("repos")
|
||||||
|
)(BranchSelector);
|
||||||
|
|||||||
@@ -111,12 +111,23 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
path={`${url}/edit`}
|
path={`${url}/edit`}
|
||||||
component={() => <Edit repository={repository} />}
|
component={() => <Edit repository={repository} />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path={`${url}/changesets`}
|
||||||
|
render={() => (
|
||||||
|
<BranchRoot
|
||||||
|
repository={repository}
|
||||||
|
baseUrlWithBranch={`${url}/branches`}
|
||||||
|
baseUrlWithoutBranch={`${url}/changesets`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/branches/:branch/changesets`}
|
path={`${url}/branches/:branch/changesets`}
|
||||||
render={() => (
|
render={() => (
|
||||||
<BranchRoot
|
<BranchRoot
|
||||||
repository={repository}
|
repository={repository}
|
||||||
baseUrl={`${url}/branches`}
|
baseUrlWithBranch={`${url}/branches`}
|
||||||
|
baseUrlWithoutBranch={`${url}/changesets`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -128,7 +139,7 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
<NavLink to={url} label={t("repository-root.information")} />
|
<NavLink to={url} label={t("repository-root.information")} />
|
||||||
<NavLink
|
<NavLink
|
||||||
activeOnlyWhenExact={false}
|
activeOnlyWhenExact={false}
|
||||||
to={`${url}/branches/master/changesets/1`}
|
to={`${url}/changesets/`}
|
||||||
label={t("repository-root.history")}
|
label={t("repository-root.history")}
|
||||||
activeWhenMatch={this.matches}
|
activeWhenMatch={this.matches}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user