mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 09:25:43 +01:00
merged
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 ErrorBoundary } from "./ErrorBoundary";
|
||||
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 * from "./errors";
|
||||
|
||||
@@ -1,86 +1,44 @@
|
||||
//@flow
|
||||
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 { CardColumn } from "@scm-manager/ui-components";
|
||||
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 = {
|
||||
plugin: Plugin,
|
||||
fullColumnWidth?: boolean,
|
||||
|
||||
// context props
|
||||
classes: any
|
||||
plugin: Plugin
|
||||
};
|
||||
|
||||
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() {
|
||||
const { plugin, classes, fullColumnWidth } = this.props;
|
||||
const halfColumn = fullColumnWidth ? "is-full" : "is-half";
|
||||
const overlayLinkClass = fullColumnWidth
|
||||
? "overlay-full-column"
|
||||
: "overlay-half-column";
|
||||
const { plugin } = this.props;
|
||||
const avatar = this.createAvatar(plugin);
|
||||
const footerLeft = this.createFooterLeft(plugin);
|
||||
const footerRight = this.createFooterRight(plugin);
|
||||
|
||||
// TODO: Add link to plugin page below
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"box",
|
||||
"box-link-shadow",
|
||||
"column",
|
||||
"is-clipped",
|
||||
halfColumn
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
className={classNames(overlayLinkClass, "is-plugin-page")}
|
||||
to="#"
|
||||
<CardColumn
|
||||
link="#"
|
||||
avatar={avatar}
|
||||
title={plugin.name}
|
||||
description={plugin.description}
|
||||
footerLeft={footerLeft}
|
||||
footerRight={footerRight}
|
||||
/>
|
||||
<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
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import type { PluginGroup, Plugin } from "@scm-manager/ui-types";
|
||||
import { CardColumnGroup } from "@scm-manager/ui-components";
|
||||
import type { PluginGroup } from "@scm-manager/ui-types";
|
||||
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 = {
|
||||
group: PluginGroup,
|
||||
|
||||
// context props
|
||||
classes: any
|
||||
};
|
||||
|
||||
type State = {
|
||||
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);
|
||||
group: PluginGroup
|
||||
};
|
||||
|
||||
class PluginGroupEntry extends React.Component<Props> {
|
||||
render() {
|
||||
const { group, classes } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
|
||||
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
||||
let content = null;
|
||||
if (!collapsed) {
|
||||
content = group.plugins.map((plugin, index) => {
|
||||
const fullColumnWidth = this.isFullSize(group.plugins, index);
|
||||
return (
|
||||
<PluginEntry
|
||||
plugin={plugin}
|
||||
fullColumnWidth={fullColumnWidth}
|
||||
key={index}
|
||||
/>
|
||||
);
|
||||
const { group } = this.props;
|
||||
const entries = group.plugins.map((plugin, index) => {
|
||||
return <PluginEntry plugin={plugin} 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>
|
||||
);
|
||||
return <CardColumnGroup name={group.name} elements={entries} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(PluginGroupEntry);
|
||||
export default PluginGroupEntry;
|
||||
|
||||
@@ -14,7 +14,7 @@ class PluginList extends React.Component<Props> {
|
||||
|
||||
const groups = groupByCategory(plugins);
|
||||
return (
|
||||
<div className="content">
|
||||
<div className="content is-plugin-page">
|
||||
{groups.map(group => {
|
||||
return <PluginGroupEntry group={group} key={group.name} />;
|
||||
})}
|
||||
|
||||
@@ -144,8 +144,7 @@ export default function reducer(
|
||||
|
||||
switch (action.type) {
|
||||
case FETCH_PLUGINS_SUCCESS:
|
||||
const t = normalizeByName(action.payload);
|
||||
return t;
|
||||
return normalizeByName(action.payload);
|
||||
case FETCH_PLUGIN_SUCCESS:
|
||||
return reducerByNames(state, action.payload);
|
||||
default:
|
||||
@@ -190,8 +189,3 @@ export function isFetchPluginPending(state: Object, name: string) {
|
||||
export function getFetchPluginFailure(state: Object, name: string) {
|
||||
return getFailure(state, FETCH_PLUGIN, name);
|
||||
}
|
||||
|
||||
export function getPermissionsLink(state: Object, name: string) {
|
||||
const plugin = getPlugin(state, name);
|
||||
return plugin && plugin._links ? plugin._links.permissions.href : undefined;
|
||||
}
|
||||
|
||||
@@ -23,10 +23,7 @@ import reducer, {
|
||||
isFetchPluginPending,
|
||||
getFetchPluginFailure
|
||||
} from "./plugins";
|
||||
import type {
|
||||
Plugin,
|
||||
PluginCollection
|
||||
} from "@scm-manager/ui-types";
|
||||
import type { Plugin, PluginCollection } from "@scm-manager/ui-types";
|
||||
|
||||
const groupManagerPlugin: Plugin = {
|
||||
name: "scm-groupmanager-plugin",
|
||||
@@ -37,8 +34,7 @@ const groupManagerPlugin: Plugin = {
|
||||
description: "Notify a remote webserver whenever a plugin is pushed to.",
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin"
|
||||
href: "http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -52,8 +48,7 @@ const scriptPlugin: Plugin = {
|
||||
description: "Script support for scm-manager.",
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/ui/plugins/scm-script-plugin"
|
||||
href: "http://localhost:8081/api/v2/ui/plugins/scm-script-plugin"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -67,8 +62,7 @@ const branchwpPlugin: Plugin = {
|
||||
description: "This plugin adds branch write protection for plugins.",
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/ui/plugins/scm-branchwp-plugin"
|
||||
href: "http://localhost:8081/api/v2/ui/plugins/scm-branchwp-plugin"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -166,12 +160,9 @@ describe("plugins fetch", () => {
|
||||
});
|
||||
|
||||
it("should dispatch FETCH_PLUGIN_FAILURE, if the request for scm-groupmanager-plugin by name fails", () => {
|
||||
fetchMock.getOnce(
|
||||
PLUGINS_URL + "/scm-groupmanager-plugin",
|
||||
{
|
||||
fetchMock.getOnce(PLUGINS_URL + "/scm-groupmanager-plugin", {
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
@@ -331,8 +322,7 @@ describe("plugins selectors", () => {
|
||||
it("should return true, when fetch plugin is pending", () => {
|
||||
const state = {
|
||||
pending: {
|
||||
[FETCH_PLUGIN +
|
||||
"/scm-groupmanager-plugin"]: true
|
||||
[FETCH_PLUGIN + "/scm-groupmanager-plugin"]: true
|
||||
}
|
||||
};
|
||||
expect(isFetchPluginPending(state, "scm-groupmanager-plugin")).toEqual(
|
||||
@@ -347,8 +337,7 @@ describe("plugins selectors", () => {
|
||||
it("should return error when fetch plugin did fail", () => {
|
||||
const state = {
|
||||
failure: {
|
||||
[FETCH_PLUGIN +
|
||||
"/scm-groupmanager-plugin"]: error
|
||||
[FETCH_PLUGIN + "/scm-groupmanager-plugin"]: error
|
||||
}
|
||||
};
|
||||
expect(getFetchPluginFailure(state, "scm-groupmanager-plugin")).toEqual(
|
||||
|
||||
@@ -1,33 +1,12 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import injectSheet from "react-jss";
|
||||
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 classNames from "classnames";
|
||||
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 = {
|
||||
repository: Repository,
|
||||
fullColumnWidth?: boolean,
|
||||
// context props
|
||||
classes: any
|
||||
repository: Repository
|
||||
};
|
||||
|
||||
class RepositoryEntry extends React.Component<Props> {
|
||||
@@ -83,53 +62,41 @@ class RepositoryEntry extends React.Component<Props> {
|
||||
return null;
|
||||
};
|
||||
|
||||
render() {
|
||||
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";
|
||||
createFooterLeft = (repository: Repository, repositoryLink: string) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"box",
|
||||
"box-link-shadow",
|
||||
"column",
|
||||
"is-clipped",
|
||||
halfColumn
|
||||
)}
|
||||
>
|
||||
<Link className={classNames(overlayLinkClass)} to={repositoryLink} />
|
||||
<article className={classNames("media", classes.inner)}>
|
||||
<figure className={classNames(classes.centerImage, "media-left")}>
|
||||
<RepositoryAvatar repository={repository} />
|
||||
</figure>
|
||||
<div className={classNames("media-content", "text-box")}>
|
||||
<div className="content">
|
||||
<p className="is-marginless">
|
||||
<strong>{repository.name}</strong>
|
||||
</p>
|
||||
<p className="shorten-text">{repository.description}</p>
|
||||
</div>
|
||||
<nav className="level is-mobile">
|
||||
<div className="level-left">
|
||||
<>
|
||||
{this.renderBranchesLink(repository, repositoryLink)}
|
||||
{this.renderChangesetsLink(repository, repositoryLink)}
|
||||
{this.renderSourcesLink(repository, repositoryLink)}
|
||||
{this.renderModifyLink(repository, repositoryLink)}
|
||||
</div>
|
||||
<div className="level-right is-hidden-mobile">
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
createFooterRight = (repository: Repository) => {
|
||||
return (
|
||||
<small className="level-item">
|
||||
<DateFromNow date={repository.creationDate} />
|
||||
</small>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { repository } = this.props;
|
||||
const repositoryLink = this.createLink(repository);
|
||||
const footerLeft = this.createFooterLeft(repository, repositoryLink);
|
||||
const footerRight = this.createFooterRight(repository);
|
||||
return (
|
||||
<CardColumn
|
||||
avatar={<RepositoryAvatar repository={repository} />}
|
||||
title={repository.name}
|
||||
description={repository.description}
|
||||
link={repositoryLink}
|
||||
footerLeft={footerLeft}
|
||||
footerRight={footerRight}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(RepositoryEntry);
|
||||
export default RepositoryEntry;
|
||||
|
||||
@@ -1,92 +1,21 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { RepositoryGroup, Repository } from "@scm-manager/ui-types";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import { CardColumnGroup } from "@scm-manager/ui-components";
|
||||
import type { RepositoryGroup } from "@scm-manager/ui-types";
|
||||
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 = {
|
||||
group: RepositoryGroup,
|
||||
|
||||
// context props
|
||||
classes: any
|
||||
};
|
||||
|
||||
type State = {
|
||||
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);
|
||||
group: RepositoryGroup
|
||||
};
|
||||
|
||||
class RepositoryGroupEntry extends React.Component<Props> {
|
||||
render() {
|
||||
const { group, classes } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
|
||||
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
||||
let content = null;
|
||||
if (!collapsed) {
|
||||
content = group.repositories.map((repository, index) => {
|
||||
const fullColumnWidth = this.isFullSize(group.repositories, index);
|
||||
return (
|
||||
<RepositoryEntry repository={repository} fullColumnWidth={fullColumnWidth} key={index} />
|
||||
);
|
||||
const { group } = this.props;
|
||||
const entries = group.repositories.map((repository, index) => {
|
||||
return <RepositoryEntry repository={repository} 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>
|
||||
);
|
||||
return <CardColumnGroup name={group.name} elements={entries} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(RepositoryGroupEntry);
|
||||
export default RepositoryGroupEntry;
|
||||
|
||||
@@ -159,33 +159,31 @@ ul.is-separated {
|
||||
|
||||
// multiline Columns
|
||||
.columns.is-multiline {
|
||||
.column {
|
||||
height: 120px;
|
||||
|
||||
.overlay-column {
|
||||
position: absolute;
|
||||
height: calc(120px - 1.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
.column.is-half {
|
||||
width: calc(50% - 0.75rem);
|
||||
max-height: 120px;
|
||||
|
||||
&:nth-child(odd) {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
.overlay-half-column {
|
||||
position: absolute;
|
||||
height: calc(120px - 1.5rem);
|
||||
.overlay-column {
|
||||
width: calc(50% - 3rem);
|
||||
}
|
||||
.overlay-half-column.is-plugin-page {
|
||||
width: calc(37.5% - 1.5rem);
|
||||
}
|
||||
}
|
||||
.column.is-full {
|
||||
.overlay-full-column {
|
||||
position: absolute;
|
||||
height: calc(120px - 0.5rem);
|
||||
|
||||
.column.is-full .overlay-column {
|
||||
width: calc(100% - 1.5rem);
|
||||
}
|
||||
.overlay-full-column.is-plugin-page {
|
||||
width: calc(75% - 1.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.column.is-half {
|
||||
width: 100%;
|
||||
@@ -194,16 +192,30 @@ ul.is-separated {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.overlay-half-column,
|
||||
.overlay-half-column.is-plugin-page {
|
||||
.overlay-column {
|
||||
width: calc(100% - 1.5rem);
|
||||
}
|
||||
}
|
||||
.column.is-full .overlay-full-column.is-plugin-page {
|
||||
}
|
||||
}
|
||||
.content.is-plugin-page {
|
||||
.columns.is-multiline {
|
||||
.column.is-half .overlay-column {
|
||||
width: calc(37.5% - 1.5rem);
|
||||
}
|
||||
|
||||
.column.is-full .overlay-column {
|
||||
width: calc(75% - 1.5rem);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.column.is-half .overlay-column,
|
||||
.column.is-full .overlay-column {
|
||||
width: calc(100% - 1.5rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-box {
|
||||
width: calc(50% - 0.75rem);
|
||||
|
||||
@@ -29,7 +29,7 @@ public class UIPluginDtoMapper {
|
||||
UIPluginDto dto = new UIPluginDto();
|
||||
dto.setName(plugin.getPlugin().getInformation().getName());
|
||||
dto.setBundles(getScriptResources(plugin));
|
||||
dto.setType(plugin.getPlugin().getInformation().getCategory() != null ? plugin.getPlugin().getInformation().getCategory() : "Sonstige/Miscellaneous");
|
||||
dto.setType(plugin.getPlugin().getInformation().getCategory() != null ? plugin.getPlugin().getInformation().getCategory() : "Miscellaneous");
|
||||
dto.setVersion(plugin.getPlugin().getInformation().getVersion());
|
||||
dto.setAuthor(plugin.getPlugin().getInformation().getAuthor());
|
||||
dto.setDescription(plugin.getPlugin().getInformation().getDescription());
|
||||
|
||||
Reference in New Issue
Block a user