From f2fb17d9b5eec1743705d34712e82fb8b9cd217b Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 9 Jul 2019 13:29:25 +0200 Subject: [PATCH] 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. --- .../packages/ui-components/src/CardColumn.js | 87 +++++++++++++++ .../ui-components/src/CardColumnGroup.js | 103 ++++++++++++++++++ .../packages/ui-components/src/index.js | 2 + .../admin/plugins/components/PluginEntry.js | 98 +++++------------ .../plugins/components/PluginGroupEntry.js | 95 ++-------------- .../repos/components/list/RepositoryEntry.js | 103 ++++++------------ .../components/list/RepositoryGroupEntry.js | 91 ++-------------- scm-ui/styles/scm.scss | 23 ++-- 8 files changed, 288 insertions(+), 314 deletions(-) create mode 100644 scm-ui-components/packages/ui-components/src/CardColumn.js create mode 100644 scm-ui-components/packages/ui-components/src/CardColumnGroup.js diff --git a/scm-ui-components/packages/ui-components/src/CardColumn.js b/scm-ui-components/packages/ui-components/src/CardColumn.js new file mode 100644 index 0000000000..e1eb65255a --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/CardColumn.js @@ -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 { + createLink = () => { + const { link } = this.props; + if (link) { + return ; + } + return null; + }; + + render() { + const { avatar, title, description, footerLeft, footerRight, classes } = this.props; + const link = this.createLink(); + return ( + <> + {link} +
+
+ {avatar} +
+
+
+
+

+ {title} +

+

{description}

+
+
+
+
{footerLeft}
+
{footerRight}
+
+
+
+ + ); + } +} + +export default injectSheet(styles)(CardColumn); diff --git a/scm-ui-components/packages/ui-components/src/CardColumnGroup.js b/scm-ui-components/packages/ui-components/src/CardColumnGroup.js new file mode 100644 index 0000000000..b72bd97dd7 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/CardColumnGroup.js @@ -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 { + 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 ( +
+ {entry} +
+ ); + }); + } + return ( +
+

+ + {name} + +

+
+
+ {content} +
+
+
+ ); + } +} + +export default injectSheet(styles)(CardColumnGroup); diff --git a/scm-ui-components/packages/ui-components/src/index.js b/scm-ui-components/packages/ui-components/src/index.js index ae6fd6f875..f6d83d6d08 100644 --- a/scm-ui-components/packages/ui-components/src/index.js +++ b/scm-ui-components/packages/ui-components/src/index.js @@ -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"; diff --git a/scm-ui/src/admin/plugins/components/PluginEntry.js b/scm-ui/src/admin/plugins/components/PluginEntry.js index b12c8dd1e1..a8cdaad915 100644 --- a/scm-ui/src/admin/plugins/components/PluginEntry.js +++ b/scm-ui/src/admin/plugins/components/PluginEntry.js @@ -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 { + createAvatar = (plugin: Plugin) => { + return ; + }; + + createFooterLeft = (plugin: Plugin) => { + return {plugin.author}; + }; + + createFooterRight = (plugin: Plugin) => { + return

{plugin.version}

; + }; + 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 ( -
- -
-
- -
-
-
- -

{plugin.description}

-

- {plugin.author} -

-
-
-
-
+ ); } } -export default injectSheet(styles)(PluginEntry); +export default PluginEntry; diff --git a/scm-ui/src/admin/plugins/components/PluginGroupEntry.js b/scm-ui/src/admin/plugins/components/PluginGroupEntry.js index 87076f6eaf..44046eb6ab 100644 --- a/scm-ui/src/admin/plugins/components/PluginGroupEntry.js +++ b/scm-ui/src/admin/plugins/components/PluginGroupEntry.js @@ -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 + group: PluginGroup }; -type State = { - collapsed: boolean -}; - -class PluginGroupEntry extends React.Component { - 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); - }; - +class PluginGroupEntry extends React.Component { 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 ( - - ); - }); - } - return ( -
-

- - {group.name} - -

-
-
- {content} -
-
-
- ); + const { group } = this.props; + const entries = group.plugins.map((plugin, index) => { + return ; + }); + return ; } } -export default injectSheet(styles)(PluginGroupEntry); +export default PluginGroupEntry; diff --git a/scm-ui/src/repos/components/list/RepositoryEntry.js b/scm-ui/src/repos/components/list/RepositoryEntry.js index eb3b3d95f6..471f900aa1 100644 --- a/scm-ui/src/repos/components/list/RepositoryEntry.js +++ b/scm-ui/src/repos/components/list/RepositoryEntry.js @@ -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 { @@ -83,53 +62,41 @@ class RepositoryEntry extends React.Component { 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 ( -
- -
-
- -
-
-
-

- {repository.name} -

-

{repository.description}

-
- -
-
-
+ <> + {this.renderBranchesLink(repository, repositoryLink)} + {this.renderChangesetsLink(repository, repositoryLink)} + {this.renderSourcesLink(repository, repositoryLink)} + {this.renderModifyLink(repository, repositoryLink)} + + ); + }; + + createFooterRight = (repository: Repository) => { + return ( + + + + ); + }; + + render() { + const { repository } = this.props; + const repositoryLink = this.createLink(repository); + const footerLeft = this.createFooterLeft(repository, repositoryLink); + const footerRight = this.createFooterRight(repository); + return ( + } + title={repository.name} + description={repository.description} + link={repositoryLink} + footerLeft={footerLeft} + footerRight={footerRight} + /> ); } } -export default injectSheet(styles)(RepositoryEntry); +export default RepositoryEntry; diff --git a/scm-ui/src/repos/components/list/RepositoryGroupEntry.js b/scm-ui/src/repos/components/list/RepositoryGroupEntry.js index 98e7925150..8f7e25dcbe 100644 --- a/scm-ui/src/repos/components/list/RepositoryGroupEntry.js +++ b/scm-ui/src/repos/components/list/RepositoryGroupEntry.js @@ -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 + group: RepositoryGroup }; -type State = { - collapsed: boolean -}; - -class RepositoryGroupEntry extends React.Component { - 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); - }; - +class RepositoryGroupEntry extends React.Component { 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 ( - - ); - }); - } - return ( -
-

- - {group.name} - -

-
-
- {content} -
-
-
- ); + const { group } = this.props; + const entries = group.repositories.map((repository, index) => { + return ; + }); + return ; } } -export default injectSheet(styles)(RepositoryGroupEntry); +export default RepositoryGroupEntry; diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index c9d423a9e7..222090cef4 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -159,30 +159,33 @@ ul.is-separated { // multiline Columns .columns.is-multiline { + .column.is-half { + height: 120px; width: calc(50% - 0.75rem); - max-height: 120px; &:nth-child(odd) { margin-right: 1.5rem; } - .overlay-half-column { + .overlay-column { position: absolute; - height: calc(120px - 1.5rem); + height: 120px; width: calc(50% - 3rem); } - .overlay-half-column.is-plugin-page { + .overlay-column.is-plugin-page { width: calc(37.5% - 1.5rem); } } .column.is-full { - .overlay-full-column { + height: 120px; + + .overlay-column { position: absolute; - height: calc(120px - 0.5rem); + height: 120px; width: calc(100% - 1.5rem); } - .overlay-full-column.is-plugin-page { + .overlay-column.is-plugin-page { width: calc(75% - 1.5rem); } } @@ -194,12 +197,12 @@ ul.is-separated { margin-right: 0; } - .overlay-half-column, - .overlay-half-column.is-plugin-page { + .overlay-column, + .overlay-column.is-plugin-page { 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); } }