mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
use same components for plugin and repository overview
Created CardColumn and CardColumnGroup which encapsulate the layout for the two column card layout and use them for repository and plugin overview.
This commit is contained in:
87
scm-ui-components/packages/ui-components/src/CardColumn.js
Normal file
87
scm-ui-components/packages/ui-components/src/CardColumn.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//@flow
|
||||||
|
import * as React from "react";
|
||||||
|
import injectSheet from "react-jss";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
inner: {
|
||||||
|
position: "relative",
|
||||||
|
pointerEvents: "none",
|
||||||
|
zIndex: 1
|
||||||
|
},
|
||||||
|
innerLink: {
|
||||||
|
pointerEvents: "all"
|
||||||
|
},
|
||||||
|
centerImage: {
|
||||||
|
marginTop: "0.8em",
|
||||||
|
marginLeft: "1em !important"
|
||||||
|
},
|
||||||
|
flexFullHeight: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignSelf: "stretch"
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
display: "flex",
|
||||||
|
flexGrow: 1
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
display: "flex",
|
||||||
|
marginTop: "auto",
|
||||||
|
paddingBottom: "1.5rem"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title: string,
|
||||||
|
description: string,
|
||||||
|
avatar: React.Node,
|
||||||
|
footerLeft: React.Node,
|
||||||
|
footerRight: React.Node,
|
||||||
|
link: string,
|
||||||
|
// context props
|
||||||
|
classes: any
|
||||||
|
};
|
||||||
|
|
||||||
|
class CardColumn extends React.Component<Props> {
|
||||||
|
createLink = () => {
|
||||||
|
const { link } = this.props;
|
||||||
|
if (link) {
|
||||||
|
return <Link className="overlay-column" to={link} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { avatar, title, description, footerLeft, footerRight, classes } = this.props;
|
||||||
|
const link = this.createLink();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{link}
|
||||||
|
<article className={classNames("media", classes.inner)}>
|
||||||
|
<figure className={classNames(classes.centerImage, "media-left")}>
|
||||||
|
{avatar}
|
||||||
|
</figure>
|
||||||
|
<div className={classNames("media-content", "text-box", classes.flexFullHeight)}>
|
||||||
|
<div className={classes.content}>
|
||||||
|
<div className="content shorten-text">
|
||||||
|
<p className="is-marginless">
|
||||||
|
<strong>{title}</strong>
|
||||||
|
</p>
|
||||||
|
<p className="shorten-text">{description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classNames(classes.footer, "level")}>
|
||||||
|
<div className="level-left is-hidden-mobile">{footerLeft}</div>
|
||||||
|
<div className="level-right is-mobile">{footerRight}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectSheet(styles)(CardColumn);
|
||||||
103
scm-ui-components/packages/ui-components/src/CardColumnGroup.js
Normal file
103
scm-ui-components/packages/ui-components/src/CardColumnGroup.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
//@flow
|
||||||
|
import * as React from "react";
|
||||||
|
import injectSheet from "react-jss";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
pointer: {
|
||||||
|
cursor: "pointer",
|
||||||
|
fontSize: "1.5rem"
|
||||||
|
},
|
||||||
|
repoGroup: {
|
||||||
|
marginBottom: "1em"
|
||||||
|
},
|
||||||
|
wrapper: {
|
||||||
|
padding: "0 0.75rem"
|
||||||
|
},
|
||||||
|
clearfix: {
|
||||||
|
clear: "both"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
name: string,
|
||||||
|
elements: React.Node[],
|
||||||
|
|
||||||
|
// context props
|
||||||
|
classes: any
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
collapsed: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
class CardColumnGroup extends React.Component<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
collapsed: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCollapse = () => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
collapsed: !prevState.collapsed
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
isLastEntry = (array: React.Node[], index: number) => {
|
||||||
|
return index === array.length - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
isLengthOdd = (array: React.Node[]) => {
|
||||||
|
return array.length % 2 !== 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
isFullSize = (array: React.Node[], index: number) => {
|
||||||
|
return this.isLastEntry(array, index) && this.isLengthOdd(array);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { name, elements, classes } = this.props;
|
||||||
|
const { collapsed } = this.state;
|
||||||
|
|
||||||
|
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
||||||
|
let content = null;
|
||||||
|
if (!collapsed) {
|
||||||
|
content = elements.map((entry, index) => {
|
||||||
|
const fullColumnWidth = this.isFullSize(elements, index);
|
||||||
|
const sizeClass = fullColumnWidth ? "is-full" : "is-half";
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
"box",
|
||||||
|
"box-link-shadow",
|
||||||
|
"column",
|
||||||
|
"is-clipped",
|
||||||
|
sizeClass
|
||||||
|
)}
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
{entry}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={classes.repoGroup}>
|
||||||
|
<h2>
|
||||||
|
<span className={classes.pointer} onClick={this.toggleCollapse}>
|
||||||
|
<i className={classNames("fa", icon)} /> {name}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<hr />
|
||||||
|
<div className={classNames("columns", "is-multiline", classes.wrapper)}>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
<div className={classes.clearfix} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectSheet(styles)(CardColumnGroup);
|
||||||
@@ -32,6 +32,8 @@ export { default as MarkdownView } from "./MarkdownView";
|
|||||||
export { default as SyntaxHighlighter } from "./SyntaxHighlighter";
|
export { default as SyntaxHighlighter } from "./SyntaxHighlighter";
|
||||||
export { default as ErrorBoundary } from "./ErrorBoundary";
|
export { default as ErrorBoundary } from "./ErrorBoundary";
|
||||||
export { default as OverviewPageActions } from "./OverviewPageActions.js";
|
export { default as OverviewPageActions } from "./OverviewPageActions.js";
|
||||||
|
export { default as CardColumnGroup } from "./CardColumnGroup";
|
||||||
|
export { default as CardColumn } from "./CardColumn";
|
||||||
|
|
||||||
export { apiClient } from "./apiclient.js";
|
export { apiClient } from "./apiclient.js";
|
||||||
export * from "./errors";
|
export * from "./errors";
|
||||||
|
|||||||
@@ -1,86 +1,44 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import injectSheet from "react-jss";
|
|
||||||
import classNames from "classnames";
|
|
||||||
import type { Plugin } from "@scm-manager/ui-types";
|
import type { Plugin } from "@scm-manager/ui-types";
|
||||||
|
import { CardColumn } from "@scm-manager/ui-components";
|
||||||
import PluginAvatar from "./PluginAvatar";
|
import PluginAvatar from "./PluginAvatar";
|
||||||
|
|
||||||
const styles = {
|
|
||||||
inner: {
|
|
||||||
position: "relative",
|
|
||||||
pointerEvents: "none",
|
|
||||||
zIndex: 1
|
|
||||||
},
|
|
||||||
centerImage: {
|
|
||||||
marginTop: "0.8em",
|
|
||||||
marginLeft: "1em !important"
|
|
||||||
},
|
|
||||||
marginBottom: {
|
|
||||||
marginBottom: "0.75rem !important"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
plugin: Plugin,
|
plugin: Plugin
|
||||||
fullColumnWidth?: boolean,
|
|
||||||
|
|
||||||
// context props
|
|
||||||
classes: any
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PluginEntry extends React.Component<Props> {
|
class PluginEntry extends React.Component<Props> {
|
||||||
|
createAvatar = (plugin: Plugin) => {
|
||||||
|
return <PluginAvatar plugin={plugin} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
createFooterLeft = (plugin: Plugin) => {
|
||||||
|
return <small className="level-item">{plugin.author}</small>;
|
||||||
|
};
|
||||||
|
|
||||||
|
createFooterRight = (plugin: Plugin) => {
|
||||||
|
return <p className="level-item">{plugin.version}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { plugin, classes, fullColumnWidth } = this.props;
|
const { plugin } = this.props;
|
||||||
const halfColumn = fullColumnWidth ? "is-full" : "is-half";
|
const avatar = this.createAvatar(plugin);
|
||||||
const overlayLinkClass = fullColumnWidth
|
const footerLeft = this.createFooterLeft(plugin);
|
||||||
? "overlay-full-column"
|
const footerRight = this.createFooterRight(plugin);
|
||||||
: "overlay-half-column";
|
|
||||||
// TODO: Add link to plugin page below
|
// TODO: Add link to plugin page below
|
||||||
return (
|
return (
|
||||||
<div
|
<CardColumn
|
||||||
className={classNames(
|
link="#"
|
||||||
"box",
|
avatar={avatar}
|
||||||
"box-link-shadow",
|
title={plugin.name}
|
||||||
"column",
|
description={plugin.description}
|
||||||
"is-clipped",
|
footerLeft={footerLeft}
|
||||||
halfColumn
|
footerRight={footerRight}
|
||||||
)}
|
/>
|
||||||
>
|
|
||||||
<Link
|
|
||||||
className={classNames(overlayLinkClass, "is-plugin-page")}
|
|
||||||
to="#"
|
|
||||||
/>
|
|
||||||
<article className={classNames("media", classes.inner)}>
|
|
||||||
<figure className={classNames(classes.centerImage, "media-left")}>
|
|
||||||
<PluginAvatar plugin={plugin} />
|
|
||||||
</figure>
|
|
||||||
<div className={classNames("media-content", "text-box")}>
|
|
||||||
<div className="content">
|
|
||||||
<nav
|
|
||||||
className={classNames(
|
|
||||||
"level",
|
|
||||||
"is-mobile",
|
|
||||||
classes.marginBottom
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="level-left">
|
|
||||||
<strong>{plugin.name}</strong>
|
|
||||||
</div>
|
|
||||||
<div className="level-right is-hidden-mobile">
|
|
||||||
{plugin.version}
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<p className="shorten-text is-marginless">{plugin.description}</p>
|
|
||||||
<p>
|
|
||||||
<small>{plugin.author}</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(PluginEntry);
|
export default PluginEntry;
|
||||||
|
|||||||
@@ -1,96 +1,21 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import injectSheet from "react-jss";
|
import { CardColumnGroup } from "@scm-manager/ui-components";
|
||||||
import classNames from "classnames";
|
import type { PluginGroup } from "@scm-manager/ui-types";
|
||||||
import type { PluginGroup, Plugin } from "@scm-manager/ui-types";
|
|
||||||
import PluginEntry from "./PluginEntry";
|
import PluginEntry from "./PluginEntry";
|
||||||
|
|
||||||
const styles = {
|
|
||||||
pointer: {
|
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: "1.5rem"
|
|
||||||
},
|
|
||||||
pluginGroup: {
|
|
||||||
marginBottom: "1em"
|
|
||||||
},
|
|
||||||
wrapper: {
|
|
||||||
padding: "0 0.75rem"
|
|
||||||
},
|
|
||||||
clearfix: {
|
|
||||||
clear: "both"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
group: PluginGroup,
|
group: PluginGroup
|
||||||
|
|
||||||
// context props
|
|
||||||
classes: any
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
class PluginGroupEntry extends React.Component<Props> {
|
||||||
collapsed: boolean
|
|
||||||
};
|
|
||||||
|
|
||||||
class PluginGroupEntry extends React.Component<Props, State> {
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
collapsed: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleCollapse = () => {
|
|
||||||
this.setState(prevState => ({
|
|
||||||
collapsed: !prevState.collapsed
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
isLastEntry = (array: Plugin[], index: number) => {
|
|
||||||
return index === array.length - 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
isLengthOdd = (array: Plugin[]) => {
|
|
||||||
return array.length % 2 !== 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
isFullSize = (array: Plugin[], index: number) => {
|
|
||||||
return this.isLastEntry(array, index) && this.isLengthOdd(array);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { group, classes } = this.props;
|
const { group } = this.props;
|
||||||
const { collapsed } = this.state;
|
const entries = group.plugins.map((plugin, index) => {
|
||||||
|
return <PluginEntry plugin={plugin} key={index} />;
|
||||||
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
});
|
||||||
let content = null;
|
return <CardColumnGroup name={group.name} elements={entries} />;
|
||||||
if (!collapsed) {
|
|
||||||
content = group.plugins.map((plugin, index) => {
|
|
||||||
const fullColumnWidth = this.isFullSize(group.plugins, index);
|
|
||||||
return (
|
|
||||||
<PluginEntry
|
|
||||||
plugin={plugin}
|
|
||||||
fullColumnWidth={fullColumnWidth}
|
|
||||||
key={index}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={classes.pluginGroup}>
|
|
||||||
<h2>
|
|
||||||
<span className={classes.pointer} onClick={this.toggleCollapse}>
|
|
||||||
<i className={classNames("fa", icon)} /> {group.name}
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
<hr />
|
|
||||||
<div className={classNames("columns", "is-multiline", classes.wrapper)}>
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
<div className={classes.clearfix} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(PluginGroupEntry);
|
export default PluginGroupEntry;
|
||||||
|
|||||||
@@ -1,33 +1,12 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import injectSheet from "react-jss";
|
|
||||||
import type { Repository } from "@scm-manager/ui-types";
|
import type { Repository } from "@scm-manager/ui-types";
|
||||||
import { DateFromNow } from "@scm-manager/ui-components";
|
import { CardColumn, DateFromNow } from "@scm-manager/ui-components";
|
||||||
import RepositoryEntryLink from "./RepositoryEntryLink";
|
import RepositoryEntryLink from "./RepositoryEntryLink";
|
||||||
import classNames from "classnames";
|
|
||||||
import RepositoryAvatar from "./RepositoryAvatar";
|
import RepositoryAvatar from "./RepositoryAvatar";
|
||||||
|
|
||||||
const styles = {
|
|
||||||
inner: {
|
|
||||||
position: "relative",
|
|
||||||
pointerEvents: "none",
|
|
||||||
zIndex: 1
|
|
||||||
},
|
|
||||||
innerLink: {
|
|
||||||
pointerEvents: "all"
|
|
||||||
},
|
|
||||||
centerImage: {
|
|
||||||
marginTop: "0.8em",
|
|
||||||
marginLeft: "1em !important"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository,
|
repository: Repository
|
||||||
fullColumnWidth?: boolean,
|
|
||||||
// context props
|
|
||||||
classes: any
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RepositoryEntry extends React.Component<Props> {
|
class RepositoryEntry extends React.Component<Props> {
|
||||||
@@ -83,53 +62,41 @@ class RepositoryEntry extends React.Component<Props> {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
createFooterLeft = (repository: Repository, repositoryLink: string) => {
|
||||||
const { repository, classes, fullColumnWidth } = this.props;
|
|
||||||
const repositoryLink = this.createLink(repository);
|
|
||||||
const halfColumn = fullColumnWidth ? "is-full" : "is-half";
|
|
||||||
const overlayLinkClass = fullColumnWidth
|
|
||||||
? "overlay-full-column"
|
|
||||||
: "overlay-half-column";
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className={classNames(
|
{this.renderBranchesLink(repository, repositoryLink)}
|
||||||
"box",
|
{this.renderChangesetsLink(repository, repositoryLink)}
|
||||||
"box-link-shadow",
|
{this.renderSourcesLink(repository, repositoryLink)}
|
||||||
"column",
|
{this.renderModifyLink(repository, repositoryLink)}
|
||||||
"is-clipped",
|
</>
|
||||||
halfColumn
|
);
|
||||||
)}
|
};
|
||||||
>
|
|
||||||
<Link className={classNames(overlayLinkClass)} to={repositoryLink} />
|
createFooterRight = (repository: Repository) => {
|
||||||
<article className={classNames("media", classes.inner)}>
|
return (
|
||||||
<figure className={classNames(classes.centerImage, "media-left")}>
|
<small className="level-item">
|
||||||
<RepositoryAvatar repository={repository} />
|
<DateFromNow date={repository.creationDate} />
|
||||||
</figure>
|
</small>
|
||||||
<div className={classNames("media-content", "text-box")}>
|
);
|
||||||
<div className="content">
|
};
|
||||||
<p className="is-marginless">
|
|
||||||
<strong>{repository.name}</strong>
|
render() {
|
||||||
</p>
|
const { repository } = this.props;
|
||||||
<p className="shorten-text">{repository.description}</p>
|
const repositoryLink = this.createLink(repository);
|
||||||
</div>
|
const footerLeft = this.createFooterLeft(repository, repositoryLink);
|
||||||
<nav className="level is-mobile">
|
const footerRight = this.createFooterRight(repository);
|
||||||
<div className="level-left">
|
return (
|
||||||
{this.renderBranchesLink(repository, repositoryLink)}
|
<CardColumn
|
||||||
{this.renderChangesetsLink(repository, repositoryLink)}
|
avatar={<RepositoryAvatar repository={repository} />}
|
||||||
{this.renderSourcesLink(repository, repositoryLink)}
|
title={repository.name}
|
||||||
{this.renderModifyLink(repository, repositoryLink)}
|
description={repository.description}
|
||||||
</div>
|
link={repositoryLink}
|
||||||
<div className="level-right is-hidden-mobile">
|
footerLeft={footerLeft}
|
||||||
<small className="level-item">
|
footerRight={footerRight}
|
||||||
<DateFromNow date={repository.creationDate} />
|
/>
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(RepositoryEntry);
|
export default RepositoryEntry;
|
||||||
|
|||||||
@@ -1,92 +1,21 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RepositoryGroup, Repository } from "@scm-manager/ui-types";
|
import { CardColumnGroup } from "@scm-manager/ui-components";
|
||||||
import injectSheet from "react-jss";
|
import type { RepositoryGroup } from "@scm-manager/ui-types";
|
||||||
import classNames from "classnames";
|
|
||||||
import RepositoryEntry from "./RepositoryEntry";
|
import RepositoryEntry from "./RepositoryEntry";
|
||||||
|
|
||||||
const styles = {
|
|
||||||
pointer: {
|
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: "1.5rem"
|
|
||||||
},
|
|
||||||
repoGroup: {
|
|
||||||
marginBottom: "1em"
|
|
||||||
},
|
|
||||||
wrapper: {
|
|
||||||
padding: "0 0.75rem"
|
|
||||||
},
|
|
||||||
clearfix: {
|
|
||||||
clear: "both"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
group: RepositoryGroup,
|
group: RepositoryGroup
|
||||||
|
|
||||||
// context props
|
|
||||||
classes: any
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
class RepositoryGroupEntry extends React.Component<Props> {
|
||||||
collapsed: boolean
|
|
||||||
};
|
|
||||||
|
|
||||||
class RepositoryGroupEntry extends React.Component<Props, State> {
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
collapsed: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleCollapse = () => {
|
|
||||||
this.setState(prevState => ({
|
|
||||||
collapsed: !prevState.collapsed
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
isLastEntry = (array: Repository[], index: number) => {
|
|
||||||
return index === array.length - 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
isLengthOdd = (array: Repository[]) => {
|
|
||||||
return array.length % 2 !== 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
isFullSize = (array: Repository[], index: number) => {
|
|
||||||
return this.isLastEntry(array, index) && this.isLengthOdd(array);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { group, classes } = this.props;
|
const { group } = this.props;
|
||||||
const { collapsed } = this.state;
|
const entries = group.repositories.map((repository, index) => {
|
||||||
|
return <RepositoryEntry repository={repository} key={index} />;
|
||||||
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
});
|
||||||
let content = null;
|
return <CardColumnGroup name={group.name} elements={entries} />;
|
||||||
if (!collapsed) {
|
|
||||||
content = group.repositories.map((repository, index) => {
|
|
||||||
const fullColumnWidth = this.isFullSize(group.repositories, index);
|
|
||||||
return (
|
|
||||||
<RepositoryEntry repository={repository} fullColumnWidth={fullColumnWidth} key={index} />
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={classes.repoGroup}>
|
|
||||||
<h2>
|
|
||||||
<span className={classes.pointer} onClick={this.toggleCollapse}>
|
|
||||||
<i className={classNames("fa", icon)} /> {group.name}
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
<hr />
|
|
||||||
<div className={classNames("columns", "is-multiline", classes.wrapper)}>
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
<div className={classes.clearfix} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(RepositoryGroupEntry);
|
export default RepositoryGroupEntry;
|
||||||
|
|||||||
@@ -159,30 +159,33 @@ ul.is-separated {
|
|||||||
|
|
||||||
// multiline Columns
|
// multiline Columns
|
||||||
.columns.is-multiline {
|
.columns.is-multiline {
|
||||||
|
|
||||||
.column.is-half {
|
.column.is-half {
|
||||||
|
height: 120px;
|
||||||
width: calc(50% - 0.75rem);
|
width: calc(50% - 0.75rem);
|
||||||
max-height: 120px;
|
|
||||||
|
|
||||||
&:nth-child(odd) {
|
&:nth-child(odd) {
|
||||||
margin-right: 1.5rem;
|
margin-right: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-half-column {
|
.overlay-column {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: calc(120px - 1.5rem);
|
height: 120px;
|
||||||
width: calc(50% - 3rem);
|
width: calc(50% - 3rem);
|
||||||
}
|
}
|
||||||
.overlay-half-column.is-plugin-page {
|
.overlay-column.is-plugin-page {
|
||||||
width: calc(37.5% - 1.5rem);
|
width: calc(37.5% - 1.5rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.column.is-full {
|
.column.is-full {
|
||||||
.overlay-full-column {
|
height: 120px;
|
||||||
|
|
||||||
|
.overlay-column {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: calc(120px - 0.5rem);
|
height: 120px;
|
||||||
width: calc(100% - 1.5rem);
|
width: calc(100% - 1.5rem);
|
||||||
}
|
}
|
||||||
.overlay-full-column.is-plugin-page {
|
.overlay-column.is-plugin-page {
|
||||||
width: calc(75% - 1.5rem);
|
width: calc(75% - 1.5rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,12 +197,12 @@ ul.is-separated {
|
|||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-half-column,
|
.overlay-column,
|
||||||
.overlay-half-column.is-plugin-page {
|
.overlay-column.is-plugin-page {
|
||||||
width: calc(100% - 1.5rem);
|
width: calc(100% - 1.5rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.column.is-full .overlay-full-column.is-plugin-page {
|
.column.is-full .overlay-column.is-plugin-page {
|
||||||
width: calc(100% - 1.5rem);
|
width: calc(100% - 1.5rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user