mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-12-21 15:59:48 +01:00
Replace styled-components with bulma helpers (#1783)
Use Bulma helpers whenever possible instead of custom styled components. This pull request replaces primarily color definitions, spacing and flex instructions.
This commit is contained in:
@@ -44,10 +44,6 @@ const MinWidthControl = styled.div`
|
||||
min-width: 10rem;
|
||||
`;
|
||||
|
||||
const NoBottomMarginField = styled.div`
|
||||
margin-bottom: 0 !important;
|
||||
`;
|
||||
|
||||
const BranchSelector: FC<Props> = ({ branches, onSelectBranch, selectedBranch, label, disabled }) => {
|
||||
if (branches) {
|
||||
return (
|
||||
@@ -56,7 +52,7 @@ const BranchSelector: FC<Props> = ({ branches, onSelectBranch, selectedBranch, l
|
||||
<label className={classNames("label", "is-size-6")}>{label}</label>
|
||||
</ZeroflexFieldLabel>
|
||||
<div className="field-body">
|
||||
<NoBottomMarginField className={classNames("field", "is-narrow")}>
|
||||
<div className={classNames("field", "is-narrow", "mb-0")}>
|
||||
<MinWidthControl className="control">
|
||||
<Select
|
||||
className="is-fullwidth"
|
||||
@@ -67,7 +63,7 @@ const BranchSelector: FC<Props> = ({ branches, onSelectBranch, selectedBranch, l
|
||||
addValueToOptions={true}
|
||||
/>
|
||||
</MinWidthControl>
|
||||
</NoBottomMarginField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
@@ -30,6 +29,7 @@ import repository from "./__resources__/repository";
|
||||
// @ts-ignore ignore unknown png
|
||||
import Git from "./__resources__/git-logo.png";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import Icon from "./Icon";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 2rem;
|
||||
@@ -42,10 +42,15 @@ const longPath =
|
||||
"dream-path/src/main/scm-plugins/javaUtilityHomeHousingLinkReferrer/sonia/scm/repositoryUndergroundSupportManager/spi/SvnRepositoryServiceResolver.java";
|
||||
const baseUrl = "scm-manager.org/scm/repo/hitchhiker/heartOfGold/sources";
|
||||
const sources = Git;
|
||||
const prefix = (
|
||||
<a href="#link">
|
||||
<Icon name="heart" color="danger" />
|
||||
</a>
|
||||
);
|
||||
|
||||
storiesOf("BreadCrumb", module)
|
||||
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.addDecorator(storyFn => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.addDecorator((storyFn) => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.add("Default", () => (
|
||||
<Breadcrumb
|
||||
repository={repository}
|
||||
@@ -69,4 +74,17 @@ storiesOf("BreadCrumb", module)
|
||||
revision={"1"}
|
||||
permalink={"/" + longPath}
|
||||
/>
|
||||
))
|
||||
.add("With prefix button", () => (
|
||||
<Breadcrumb
|
||||
repository={repository}
|
||||
defaultBranch={master}
|
||||
branch={master}
|
||||
path={path}
|
||||
baseUrl={baseUrl}
|
||||
sources={sources}
|
||||
revision={"1"}
|
||||
permalink={"/" + path}
|
||||
preButtons={prefix}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -93,14 +93,7 @@ const HomeIcon = styled(Icon)`
|
||||
`;
|
||||
|
||||
const ActionBar = styled.div`
|
||||
align-self: center;
|
||||
|
||||
/* order actionbar items horizontal */
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
/* ensure space between action bar items */
|
||||
|
||||
& > * {
|
||||
/*
|
||||
* We have to use important, because plugins could use field or control classes like the editor-plugin does.
|
||||
@@ -113,7 +106,6 @@ const ActionBar = styled.div`
|
||||
|
||||
const PrefixButton = styled.div`
|
||||
border-right: 1px solid lightgray;
|
||||
margin-right: 0.5rem;
|
||||
`;
|
||||
|
||||
const Breadcrumb: FC<Props> = ({
|
||||
@@ -176,7 +168,7 @@ const Breadcrumb: FC<Props> = ({
|
||||
const renderBreadcrumbNav = () => {
|
||||
let prefixButtons = null;
|
||||
if (preButtons) {
|
||||
prefixButtons = <PrefixButton>{preButtons}</PrefixButton>;
|
||||
prefixButtons = <PrefixButton className="mr-2">{preButtons}</PrefixButton>;
|
||||
}
|
||||
|
||||
let homeUrl = baseUrl + "/";
|
||||
@@ -227,13 +219,7 @@ const Breadcrumb: FC<Props> = ({
|
||||
binder.hasExtension<extensionPoints.ReposSourcesEmptyActionbar>("repos.sources.empty.actionbar") &&
|
||||
sources?._embedded?.children?.length === 0
|
||||
) {
|
||||
return (
|
||||
<ExtensionPoint
|
||||
name="repos.sources.empty.actionbar"
|
||||
props={{ repository, sources }}
|
||||
renderAll={true}
|
||||
/>
|
||||
);
|
||||
return <ExtensionPoint name="repos.sources.empty.actionbar" props={{ repository, sources }} renderAll={true} />;
|
||||
}
|
||||
if (binder.hasExtension<extensionPoints.ReposSourcesActionbar>("repos.sources.actionbar")) {
|
||||
return <ExtensionPoint name="repos.sources.actionbar" props={extProps} renderAll={true} />;
|
||||
@@ -243,11 +229,15 @@ const Breadcrumb: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="is-flex is-align-items-center is-justify-content-flex-end">
|
||||
<div className={classNames("is-flex", "is-justify-content-flex-end", "is-align-items-center")}>
|
||||
{renderBreadcrumbNav()}
|
||||
{<ActionBar className="my-2">{renderExtensionPoints()}</ActionBar>}
|
||||
{
|
||||
<ActionBar className={classNames("is-flex", "is-justify-content-flex-start", "is-align-self-center", "my-2")}>
|
||||
{renderExtensionPoints()}
|
||||
</ActionBar>
|
||||
}
|
||||
</div>
|
||||
<hr className="is-marginless" />
|
||||
<hr className="m-0" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,29 +44,6 @@ const NoEventWrapper = styled.article`
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
const AvatarWrapper = styled.figure`
|
||||
margin-top: 0.8em;
|
||||
margin-left: 1em !important;
|
||||
`;
|
||||
|
||||
const FlexFullHeight = styled.div`
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-self: stretch;
|
||||
`;
|
||||
|
||||
const FooterWrapper = styled.div`
|
||||
padding-bottom: 1rem;
|
||||
`;
|
||||
|
||||
const ContentRight = styled.div`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
const RightMarginDiv = styled.div`
|
||||
margin-right: 0.5rem;
|
||||
`;
|
||||
|
||||
const InheritFlexShrinkDiv = styled.div`
|
||||
flex-shrink: inherit;
|
||||
pointer-events: all;
|
||||
@@ -81,11 +58,11 @@ const CardColumn: FC<Props> = ({
|
||||
footerLeft,
|
||||
footerRight,
|
||||
action,
|
||||
className
|
||||
className,
|
||||
}) => {
|
||||
const renderAvatar = avatar ? <AvatarWrapper className="media-left">{avatar}</AvatarWrapper> : null;
|
||||
const renderAvatar = avatar ? <figure className="media-left mt-3 ml-4">{avatar}</figure> : null;
|
||||
const renderDescription = description ? <p className="shorten-text">{description}</p> : null;
|
||||
const renderContentRight = contentRight ? <ContentRight>{contentRight}</ContentRight> : null;
|
||||
const renderContentRight = contentRight ? <div className="ml-auto">{contentRight}</div> : null;
|
||||
|
||||
let createLink = null;
|
||||
if (link) {
|
||||
@@ -94,7 +71,7 @@ const CardColumn: FC<Props> = ({
|
||||
createLink = (
|
||||
<a
|
||||
className="overlay-column"
|
||||
onClick={e => {
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
action();
|
||||
}}
|
||||
@@ -108,7 +85,16 @@ const CardColumn: FC<Props> = ({
|
||||
{createLink}
|
||||
<NoEventWrapper className={classNames("media", className)}>
|
||||
{renderAvatar}
|
||||
<FlexFullHeight className={classNames("media-content", "text-box", "is-flex")}>
|
||||
<div
|
||||
className={classNames(
|
||||
"media-content",
|
||||
"text-box",
|
||||
"is-flex",
|
||||
"is-flex-direction-column",
|
||||
"is-justify-content-space-around",
|
||||
"is-align-self-stretch"
|
||||
)}
|
||||
>
|
||||
<div className="is-flex">
|
||||
<div className="is-clipped mb-0">
|
||||
<p className="shorten-text m-0">{title}</p>
|
||||
@@ -116,13 +102,13 @@ const CardColumn: FC<Props> = ({
|
||||
</div>
|
||||
{renderContentRight}
|
||||
</div>
|
||||
<FooterWrapper className={classNames("level", "is-flex")}>
|
||||
<RightMarginDiv className="level-left is-hidden-mobile">{footerLeft}</RightMarginDiv>
|
||||
<div className={classNames("level", "is-flex", "pb-4")}>
|
||||
<div className={classNames("level-left", "is-hidden-mobile", "mr-2")}>{footerLeft}</div>
|
||||
<InheritFlexShrinkDiv className="level-right is-block is-mobile m-0 shorten-text">
|
||||
{footerRight}
|
||||
</InheritFlexShrinkDiv>
|
||||
</FooterWrapper>
|
||||
</FlexFullHeight>
|
||||
</div>
|
||||
</div>
|
||||
</NoEventWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
name: ReactNode;
|
||||
@@ -36,25 +35,17 @@ type State = {
|
||||
collapsed: boolean;
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
margin-bottom: 1em;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding: 0 0.75rem;
|
||||
`;
|
||||
|
||||
export default class CardColumnGroup extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
collapsed: false
|
||||
collapsed: false,
|
||||
};
|
||||
}
|
||||
|
||||
toggleCollapse = () => {
|
||||
this.setState(prevState => ({
|
||||
collapsed: !prevState.collapsed
|
||||
this.setState((prevState) => ({
|
||||
collapsed: !prevState.collapsed,
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -89,13 +80,13 @@ export default class CardColumnGroup extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<div className="mb-4">
|
||||
<h2>
|
||||
<span className={classNames("is-size-4", "has-cursor-pointer")} onClick={this.toggleCollapse}>
|
||||
<span className={classNames("is-size-4", "is-clickable")} onClick={this.toggleCollapse}>
|
||||
<i className={classNames("fa", icon)} />
|
||||
</span>{" "}
|
||||
{url ? (
|
||||
<Link to={url} className={"has-text-dark"}>
|
||||
<Link to={url} className="has-text-dark">
|
||||
{name}
|
||||
</Link>
|
||||
) : (
|
||||
@@ -103,9 +94,9 @@ export default class CardColumnGroup extends React.Component<Props, State> {
|
||||
)}
|
||||
</h2>
|
||||
<hr />
|
||||
<Wrapper className={classNames("columns", "card-columns", "is-multiline")}>{content}</Wrapper>
|
||||
<div className={classNames("columns", "card-columns", "is-multiline", "mx-3", "my-0")}>{content}</div>
|
||||
<div className="is-clearfix" />
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const Wrapper = styled.div`
|
||||
|
||||
const link = "/foo/bar";
|
||||
const icon = <Icon name="icons fa-2x fa-fw" />;
|
||||
const contentLeft = <strong className="is-marginless">main content</strong>;
|
||||
const contentLeft = <strong className="m-0">main content</strong>;
|
||||
const contentRight = <small>more text</small>;
|
||||
|
||||
storiesOf("CardColumnSmall", module)
|
||||
|
||||
@@ -35,22 +35,6 @@ type Props = {
|
||||
footer?: ReactNode;
|
||||
};
|
||||
|
||||
const FlexFullHeight = styled.div`
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-self: stretch;
|
||||
`;
|
||||
|
||||
const ContentLeft = styled.div`
|
||||
margin-bottom: 0 !important;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const ContentRight = styled.div`
|
||||
margin-left: auto;
|
||||
align-items: start;
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
color: inherit;
|
||||
:hover {
|
||||
@@ -58,25 +42,30 @@ const StyledLink = styled(Link)`
|
||||
}
|
||||
`;
|
||||
|
||||
const AvatarWrapper = styled.figure`
|
||||
margin-right: 0.5rem;
|
||||
`;
|
||||
|
||||
const CardColumnSmall: FC<Props> = ({ link, avatar, contentLeft, contentRight, footer }) => {
|
||||
const renderAvatar = avatar ? <AvatarWrapper className="media-left">{avatar}</AvatarWrapper> : null;
|
||||
const renderAvatar = avatar ? <figure className={classNames("media-left", "mr-2")}>{avatar}</figure> : null;
|
||||
const renderFooter = footer ? <small>{footer}</small> : null;
|
||||
|
||||
return (
|
||||
<StyledLink to={link}>
|
||||
<div className="media">
|
||||
{renderAvatar}
|
||||
<FlexFullHeight className={classNames("media-content", "text-box", "is-flex")}>
|
||||
<div className="is-flex is-align-items-center">
|
||||
<ContentLeft>{contentLeft}</ContentLeft>
|
||||
<ContentRight>{contentRight}</ContentRight>
|
||||
<div
|
||||
className={classNames(
|
||||
"media-content",
|
||||
"text-box",
|
||||
"is-flex",
|
||||
"is-flex-direction-column",
|
||||
"is-justify-content-space-around",
|
||||
"is-align-self-stretch"
|
||||
)}
|
||||
>
|
||||
<div className={classNames("is-flex", "is-align-items-center")}>
|
||||
<div className={classNames("is-clipped", "mb-0")}>{contentLeft}</div>
|
||||
<div className={classNames("is-align-items-start", "ml-auto")}>{contentRight}</div>
|
||||
</div>
|
||||
{renderFooter}
|
||||
</FlexFullHeight>
|
||||
</div>
|
||||
</div>
|
||||
</StyledLink>
|
||||
);
|
||||
|
||||
@@ -22,43 +22,38 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React from "react";
|
||||
import DateFromNow from "./DateFromNow";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import DateFromNow from "./DateFromNow";
|
||||
import DateShort from "./DateShort";
|
||||
import styled from "styled-components";
|
||||
|
||||
const baseProps = {
|
||||
timeZone: "Europe/Berlin",
|
||||
baseDate: "2019-10-12T13:56:42+02:00"
|
||||
baseDate: "2019-10-12T13:56:42+02:00",
|
||||
};
|
||||
|
||||
const dates = [
|
||||
"2009-06-30T18:30:00+02:00",
|
||||
"2019-06-30T18:30:00+02:00",
|
||||
"2019-10-12T13:56:40+02:00",
|
||||
"2019-10-11T13:56:40+02:00"
|
||||
"2019-10-11T13:56:40+02:00",
|
||||
];
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding: 2rem;
|
||||
`;
|
||||
|
||||
storiesOf("Date", module)
|
||||
.add("Date from now", () => (
|
||||
<Wrapper>
|
||||
{dates.map(d => (
|
||||
<div className="p-5">
|
||||
{dates.map((d) => (
|
||||
<p>
|
||||
<DateFromNow date={d} {...baseProps} />
|
||||
</p>
|
||||
))}
|
||||
</Wrapper>
|
||||
</div>
|
||||
))
|
||||
.add("Short", () => (
|
||||
<Wrapper>
|
||||
{dates.map(d => (
|
||||
<div className="p-5">
|
||||
{dates.map((d) => (
|
||||
<p>
|
||||
<DateShort date={d} {...baseProps} />
|
||||
</p>
|
||||
))}
|
||||
</Wrapper>
|
||||
</div>
|
||||
));
|
||||
|
||||
@@ -22,14 +22,15 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC, ReactNode, useEffect } from "react";
|
||||
import ErrorNotification from "./ErrorNotification";
|
||||
import { MissingLinkError, urls, useIndexLink } from "@scm-manager/ui-api";
|
||||
import { RouteComponentProps, useLocation, withRouter } from "react-router-dom";
|
||||
import ErrorPage from "./ErrorPage";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { MissingLinkError, urls, useIndexLink } from "@scm-manager/ui-api";
|
||||
import ErrorNotification from "./ErrorNotification";
|
||||
import ErrorPage from "./ErrorPage";
|
||||
import { Subtitle, Title } from "./layout";
|
||||
import Icon from "./Icon";
|
||||
import styled from "styled-components";
|
||||
|
||||
type State = {
|
||||
error?: Error;
|
||||
@@ -54,9 +55,6 @@ type ErrorDisplayProps = {
|
||||
};
|
||||
|
||||
const RedirectIconContainer = styled.div`
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 256px;
|
||||
`;
|
||||
|
||||
@@ -69,7 +67,14 @@ const RedirectPage = () => {
|
||||
<div className="container">
|
||||
<Title>{t("errorBoundary.redirect.title")}</Title>
|
||||
<Subtitle>{t("errorBoundary.redirect.subtitle")}</Subtitle>
|
||||
<RedirectIconContainer className="is-flex">
|
||||
<RedirectIconContainer
|
||||
className={classNames(
|
||||
"is-flex",
|
||||
"is-flex-direction-column",
|
||||
"is-justify-content-center",
|
||||
"is-align-items-center"
|
||||
)}
|
||||
>
|
||||
<Icon name="directions" className="fa-7x" />
|
||||
</RedirectIconContainer>
|
||||
</div>
|
||||
@@ -108,7 +113,7 @@ const ErrorDisplay: FC<ErrorDisplayProps> = ({ error, errorInfo, fallback: Fallb
|
||||
|
||||
const fallbackProps = {
|
||||
error,
|
||||
errorInfo
|
||||
errorInfo,
|
||||
};
|
||||
|
||||
return <FallbackComponent {...fallbackProps} />;
|
||||
@@ -130,7 +135,7 @@ class ErrorBoundary extends React.Component<Props, State> {
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo
|
||||
errorInfo,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,20 +21,10 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import styled from "styled-components";
|
||||
import * as React from "react";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import Help from "./Help";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 5rem;
|
||||
`;
|
||||
|
||||
const Spacing = styled.div`
|
||||
margin-top: 1rem;
|
||||
`;
|
||||
|
||||
const longContent =
|
||||
"Cleverness nuclear genuine static irresponsibility invited President Zaphod\n" +
|
||||
"Beeblebrox hyperspace ship. Another custard through computer-generated universe\n" +
|
||||
@@ -42,17 +32,17 @@ const longContent =
|
||||
"imaginative generator sweep.";
|
||||
|
||||
storiesOf("Help", module)
|
||||
.addDecorator(storyFn => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.addDecorator((storyFn) => <div className="m-6">{storyFn()}</div>)
|
||||
.add("Default", () => <Help message="This is a help message" />)
|
||||
.add("Multiline", () => (
|
||||
<>
|
||||
<Spacing>
|
||||
<div className="mt-4">
|
||||
<label>With multiline (default):</label>
|
||||
<Help message={longContent} />
|
||||
</Spacing>
|
||||
<Spacing>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label>Without multiline:</label>
|
||||
<Help message={longContent} multiline={false} />
|
||||
</Spacing>
|
||||
</div>
|
||||
</>
|
||||
));
|
||||
|
||||
@@ -33,22 +33,21 @@ type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const HelpTooltip = styled(Tooltip)`
|
||||
const AbsolutePositionTooltip = styled(Tooltip)`
|
||||
position: absolute;
|
||||
padding-left: 3px;
|
||||
`;
|
||||
|
||||
const Help: FC<Props> = ({ message, multiline, className }) => (
|
||||
<HelpTooltip
|
||||
className={classNames("is-inline-block", multiline ? "has-tooltip-multiline" : undefined, className)}
|
||||
<AbsolutePositionTooltip
|
||||
className={classNames("is-inline-block", "pl-1", multiline ? "has-tooltip-multiline" : undefined, className)}
|
||||
message={message}
|
||||
>
|
||||
<HelpIcon />
|
||||
</HelpTooltip>
|
||||
</AbsolutePositionTooltip>
|
||||
);
|
||||
|
||||
Help.defaultProps = {
|
||||
multiline: true
|
||||
multiline: true,
|
||||
};
|
||||
|
||||
export default Help;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import Image from "./Image";
|
||||
|
||||
@@ -31,14 +32,10 @@ type Props = WithTranslation & {
|
||||
};
|
||||
|
||||
const Wrapper = styled.div`
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 256px;
|
||||
`;
|
||||
|
||||
const FixedSizedImage = styled(Image)`
|
||||
margin-bottom: 0.75rem;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
`;
|
||||
@@ -47,8 +44,15 @@ class Loading extends React.Component<Props> {
|
||||
render() {
|
||||
const { message, t } = this.props;
|
||||
return (
|
||||
<Wrapper className="is-flex">
|
||||
<FixedSizedImage src="/images/loading.svg" alt={t("loading.alt")} />
|
||||
<Wrapper
|
||||
className={classNames(
|
||||
"is-flex",
|
||||
"is-flex-direction-column",
|
||||
"is-justify-content-center",
|
||||
"is-align-items-center"
|
||||
)}
|
||||
>
|
||||
<FixedSizedImage className="mb-3" src="/images/loading.svg" alt={t("loading.alt")} />
|
||||
<p className="has-text-centered">{message}</p>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
@@ -60,9 +60,9 @@ const OverviewPageActions: FC<Props> = ({
|
||||
const link = createAbsoluteLink(inputLink);
|
||||
|
||||
const groupSelector = groups && (
|
||||
<div className={"column is-flex"}>
|
||||
<div className="column is-flex">
|
||||
<Select
|
||||
className={"is-fullwidth"}
|
||||
className="is-fullwidth"
|
||||
options={groups.map((g) => ({ value: g, label: g }))}
|
||||
value={currentGroup}
|
||||
onChange={groupSelected}
|
||||
@@ -89,9 +89,9 @@ const OverviewPageActions: FC<Props> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={"columns is-tablet"}>
|
||||
<div className="columns is-tablet">
|
||||
{groupSelector}
|
||||
<div className={"column"}>
|
||||
<div className="column">
|
||||
<FilterInput
|
||||
placeholder={searchPlaceholder}
|
||||
value={urls.getQueryStringFromLocation(location)}
|
||||
|
||||
@@ -21,37 +21,33 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Icon } from "@scm-manager/ui-components";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import SplitAndReplace from "./SplitAndReplace";
|
||||
import { Icon } from "@scm-manager/ui-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 2rem;
|
||||
`;
|
||||
|
||||
storiesOf("SplitAndReplace", module).add("Simple replacement", () => {
|
||||
const replacements = [
|
||||
{
|
||||
textToReplace: "'",
|
||||
replacement: <Icon name={"quote-left"} />,
|
||||
replaceAll: true
|
||||
replaceAll: true,
|
||||
},
|
||||
{
|
||||
textToReplace: "`",
|
||||
replacement: <Icon name={"quote-right"} />,
|
||||
replaceAll: true
|
||||
}
|
||||
replaceAll: true,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<Wrapper>
|
||||
<div className="m-6">
|
||||
<SplitAndReplace text={"'So this is it,` said Arthur, 'We are going to die.`"} replacements={replacements} />
|
||||
</Wrapper>
|
||||
<Wrapper>
|
||||
</div>
|
||||
<div className="m-6">
|
||||
<SplitAndReplace text={"'Yes,` said Ford, 'except... no! Wait a minute!`"} replacements={replacements} />
|
||||
</Wrapper>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC, HTMLAttributes } from "react";
|
||||
import React, { FC } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Color, Size } from "./styleConstants";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
@@ -39,19 +38,7 @@ type Props = {
|
||||
onRemove?: () => void;
|
||||
};
|
||||
|
||||
type InnerTagProps = HTMLAttributes<HTMLSpanElement> & {
|
||||
small: boolean;
|
||||
};
|
||||
|
||||
const smallMixin = css`
|
||||
font-size: 0.7rem !important;
|
||||
padding: 0.25rem !important;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const InnerTag = styled.span<InnerTagProps>`
|
||||
${(props) => props.small && smallMixin};
|
||||
`;
|
||||
const smallClassNames = classNames("p-1", "is-size-7", "has-text-weight-bold");
|
||||
|
||||
const Tag: FC<Props> = ({
|
||||
className,
|
||||
@@ -82,20 +69,26 @@ const Tag: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<InnerTag
|
||||
className={classNames("tag", `is-${color}`, `is-${size}`, className, {
|
||||
"is-outlined": outlined,
|
||||
"is-rounded": rounded,
|
||||
"has-cursor-pointer": onClick,
|
||||
})}
|
||||
<span
|
||||
className={classNames(
|
||||
"tag",
|
||||
`is-${color}`,
|
||||
`is-${size}`,
|
||||
className,
|
||||
{
|
||||
"is-outlined": outlined,
|
||||
"is-rounded": rounded,
|
||||
"is-clickable": onClick,
|
||||
},
|
||||
size === "small" && smallClassNames
|
||||
)}
|
||||
title={title}
|
||||
onClick={onClick}
|
||||
small={size === "small"}
|
||||
>
|
||||
{showIcon}
|
||||
{label}
|
||||
{children}
|
||||
</InnerTag>
|
||||
</span>
|
||||
{showDelete}
|
||||
</>
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,17 +24,16 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Button, { ButtonProps } from "./Button";
|
||||
import classNames from "classnames";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin-top: 2em;
|
||||
padding: 1em 1em;
|
||||
border: 2px solid #e9f7fd;
|
||||
`;
|
||||
|
||||
export default class CreateButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return (
|
||||
<Wrapper className="has-text-centered">
|
||||
<Wrapper className={classNames("has-text-centered", "mt-5", "p-4")}>
|
||||
<Button color="primary" {...this.props} />
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
@@ -37,20 +37,8 @@ type Props = {
|
||||
errorMessage: string;
|
||||
};
|
||||
|
||||
const StyledLevel = styled(Level)`
|
||||
align-items: stretch;
|
||||
margin-bottom: 1rem !important; // same margin as field
|
||||
`;
|
||||
|
||||
const FullWidthInputField = styled(InputField)`
|
||||
width: 100%;
|
||||
margin-right: 1.5rem;
|
||||
`;
|
||||
|
||||
const StyledField = styled.div.attrs(() => ({
|
||||
className: "field"
|
||||
}))`
|
||||
align-self: flex-end;
|
||||
`;
|
||||
|
||||
const AddEntryToTableField: FC<Props> = ({
|
||||
@@ -60,7 +48,7 @@ const AddEntryToTableField: FC<Props> = ({
|
||||
fieldLabel,
|
||||
helpText,
|
||||
validateEntry,
|
||||
errorMessage
|
||||
errorMessage,
|
||||
}) => {
|
||||
const [entryToAdd, setEntryToAdd] = useState("");
|
||||
|
||||
@@ -89,9 +77,11 @@ const AddEntryToTableField: FC<Props> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledLevel
|
||||
<Level
|
||||
className="is-align-items-stretch mb-4"
|
||||
children={
|
||||
<FullWidthInputField
|
||||
className="mr-5"
|
||||
label={fieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={handleAddEntryChange}
|
||||
@@ -103,13 +93,13 @@ const AddEntryToTableField: FC<Props> = ({
|
||||
/>
|
||||
}
|
||||
right={
|
||||
<StyledField>
|
||||
<div className="field is-align-self-flex-end">
|
||||
<AddButton
|
||||
label={buttonLabel}
|
||||
action={addButtonClicked}
|
||||
disabled={disabled || entryToAdd === "" || !isValid()}
|
||||
/>
|
||||
</StyledField>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -21,37 +21,31 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import styled from "styled-components";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
import AddKeyValueEntryToTableField from "./AddKeyValueEntryToTableField";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
|
||||
const Spacing = styled.div`
|
||||
padding: 2em;
|
||||
`;
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import AddKeyValueEntryToTableField from "./AddKeyValueEntryToTableField";
|
||||
|
||||
storiesOf("Forms|AddKeyValueEntryToTableField", module)
|
||||
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.add("Default", () => (
|
||||
<Spacing>
|
||||
<div className="m-6">
|
||||
<AddKeyValueEntryToTableField
|
||||
keyFieldLabel="Key"
|
||||
valueFieldLabel="Value"
|
||||
buttonLabel="Add to table"
|
||||
addEntry={(key, value) => {console.log(key, value)}}
|
||||
buttonLabel="Add to Table"
|
||||
addEntry={() => null}
|
||||
/>
|
||||
</Spacing>
|
||||
</div>
|
||||
))
|
||||
.add("Disabled", () => (
|
||||
<Spacing>
|
||||
<div className="m-6">
|
||||
<AddKeyValueEntryToTableField
|
||||
keyFieldLabel="Key"
|
||||
valueFieldLabel="Value"
|
||||
buttonLabel="Add to table"
|
||||
addEntry={() => {}}
|
||||
buttonLabel="Add to Table"
|
||||
addEntry={() => null}
|
||||
disabled={true}
|
||||
/>
|
||||
</Spacing>
|
||||
</div>
|
||||
));
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import InputField from "./InputField";
|
||||
@@ -39,13 +38,7 @@ type Props = {
|
||||
validateEntry?: (p: string) => boolean;
|
||||
};
|
||||
|
||||
const InputLevel = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const StyledInputField = styled(InputField)`
|
||||
margin-right: 1.5rem;
|
||||
const FullWidthInputField = styled(InputField)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@@ -62,7 +55,7 @@ const AddKeyValueEntryToTableField: FC<Props> = ({
|
||||
valueHelpText,
|
||||
validateEntry,
|
||||
errorMessage,
|
||||
addEntry
|
||||
addEntry,
|
||||
}) => {
|
||||
const [key, setKey] = useState("");
|
||||
const [value, setValue] = useState("");
|
||||
@@ -77,13 +70,14 @@ const AddKeyValueEntryToTableField: FC<Props> = ({
|
||||
|
||||
const add = () => {
|
||||
addEntry(key, value);
|
||||
setKey("")
|
||||
setKey("");
|
||||
setValue("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<InputLevel>
|
||||
<StyledInputField
|
||||
<div className="is-flex is-justify-content-space-between">
|
||||
<FullWidthInputField
|
||||
className="mr-5"
|
||||
label={keyFieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={setKey}
|
||||
@@ -93,7 +87,8 @@ const AddKeyValueEntryToTableField: FC<Props> = ({
|
||||
disabled={disabled}
|
||||
helpText={keyHelpText}
|
||||
/>
|
||||
<StyledInputField
|
||||
<FullWidthInputField
|
||||
className="mr-5"
|
||||
label={valueFieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={setValue}
|
||||
@@ -108,7 +103,7 @@ const AddKeyValueEntryToTableField: FC<Props> = ({
|
||||
action={add}
|
||||
disabled={disabled || !key || !value || !isValid(key) || !isValid(value)}
|
||||
/>
|
||||
</InputLevel>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ type Props = {
|
||||
|
||||
const FullWidthAutocomplete = styled(Autocomplete)`
|
||||
width: 100%;
|
||||
margin-right: 1.5rem;
|
||||
`;
|
||||
|
||||
const AutocompleteAddEntryToTableField: FC<Props> = ({
|
||||
@@ -79,6 +78,7 @@ const AutocompleteAddEntryToTableField: FC<Props> = ({
|
||||
<Level
|
||||
children={
|
||||
<FullWidthAutocomplete
|
||||
className="mr-5"
|
||||
label={fieldLabel}
|
||||
loadSuggestions={loadSuggestions}
|
||||
valueSelected={handleAddEntryChange}
|
||||
|
||||
@@ -22,18 +22,9 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { ChangeEvent, FC, FocusEvent } from "react";
|
||||
import { Help } from "../index";
|
||||
import styled from "styled-components";
|
||||
import { createFormFieldWrapper, FieldProps, FieldType, isLegacy, isUsingRef } from "./FormFieldTypes";
|
||||
import classNames from "classnames";
|
||||
|
||||
const StyledRadio = styled.label`
|
||||
margin-right: 0.5em;
|
||||
`;
|
||||
|
||||
const InlineFieldset = styled.fieldset`
|
||||
display: inline-block;
|
||||
`;
|
||||
import { Help } from "../index";
|
||||
import { createFormFieldWrapper, FieldProps, FieldType, isLegacy, isUsingRef } from "./FormFieldTypes";
|
||||
|
||||
type BaseProps = {
|
||||
label?: string;
|
||||
@@ -47,7 +38,12 @@ type BaseProps = {
|
||||
readOnly?: boolean;
|
||||
};
|
||||
|
||||
const InnerRadio: FC<FieldProps<BaseProps, HTMLInputElement, boolean>> = ({ name, defaultChecked, readOnly, ...props }) => {
|
||||
const InnerRadio: FC<FieldProps<BaseProps, HTMLInputElement, boolean>> = ({
|
||||
name,
|
||||
defaultChecked,
|
||||
readOnly,
|
||||
...props
|
||||
}) => {
|
||||
const renderHelp = () => {
|
||||
const helpText = props.helpText;
|
||||
if (helpText) {
|
||||
@@ -76,13 +72,13 @@ const InnerRadio: FC<FieldProps<BaseProps, HTMLInputElement, boolean>> = ({ name
|
||||
};
|
||||
|
||||
return (
|
||||
<InlineFieldset disabled={readOnly}>
|
||||
<fieldset className="is-inline-block" disabled={readOnly}>
|
||||
{/*
|
||||
we have to ignore the next line,
|
||||
because jsx label does not the custom disabled attribute
|
||||
but bulma does.
|
||||
// @ts-ignore */}
|
||||
<StyledRadio className={classNames("radio", props.className)} disabled={props.disabled}>
|
||||
<label className={classNames("radio", "mr-2", props.className)} disabled={props.disabled}>
|
||||
<input
|
||||
type="radio"
|
||||
name={name}
|
||||
@@ -96,8 +92,8 @@ const InnerRadio: FC<FieldProps<BaseProps, HTMLInputElement, boolean>> = ({ name
|
||||
/>{" "}
|
||||
{props.label}
|
||||
{renderHelp()}
|
||||
</StyledRadio>
|
||||
</InlineFieldset>
|
||||
</label>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,25 +22,21 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
title: ReactNode;
|
||||
};
|
||||
|
||||
const Title = styled.div`
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
const Menu = styled.ul`
|
||||
padding-left: 1.1rem;
|
||||
`;
|
||||
|
||||
const FooterSection: FC<Props> = ({ title, children }) => {
|
||||
return (
|
||||
<section className="column is-one-third">
|
||||
<Title>{title}</Title>
|
||||
<section className={classNames("column", "is-one-third")}>
|
||||
<div className={classNames("has-text-weight-bold", "mb-2")}>{title}</div>
|
||||
<Menu>{children}</Menu>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -21,25 +21,12 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
|
||||
const TitleWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const Separator = styled.div`
|
||||
border-bottom: 1px solid rgb(219, 219, 219, 0.5);
|
||||
margin: 0 1rem;
|
||||
`;
|
||||
|
||||
const Box = styled.div`
|
||||
padding: 0.5rem;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
@@ -51,14 +38,16 @@ const GroupEntries: FC<Props> = ({ namespaceHeader, elements }) => {
|
||||
const content = elements.map((entry, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<div>{entry}</div>
|
||||
{index + 1 !== elements.length ? <Separator /> : null}
|
||||
{index + 1 !== elements.length ? <Separator className="mx-4" /> : null}
|
||||
</React.Fragment>
|
||||
));
|
||||
|
||||
return (
|
||||
<>
|
||||
<TitleWrapper>{namespaceHeader}</TitleWrapper>
|
||||
<Box className="box">{content}</Box>
|
||||
<div className={classNames("is-flex", "is-align-items-center", "is-size-6", "has-text-weight-bold", "p-3")}>
|
||||
{namespaceHeader}
|
||||
</div>
|
||||
<div className={classNames("box", "p-2")}>{content}</div>
|
||||
<div className="is-clearfix" />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import styled from "styled-components";
|
||||
import Icon from "../Icon";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
@@ -31,16 +30,12 @@ import GroupEntry from "./GroupEntry";
|
||||
import { Button, ButtonGroup } from "../buttons";
|
||||
import copyToClipboard from "../CopyToClipboard";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 2rem;
|
||||
`;
|
||||
|
||||
const link = "/foo/bar";
|
||||
const icon = <Icon name="icons fa-2x fa-fw" />;
|
||||
const name = <strong className="is-marginless">main content</strong>;
|
||||
const name = <strong className="m-0">main content</strong>;
|
||||
const description = <small>more text</small>;
|
||||
const longName = (
|
||||
<strong className="is-marginless">
|
||||
<strong className="m-0">
|
||||
Very-important-repository-with-a-particular-long-but-easily-rememberable-name-which-also-is-written-in-kebab-case
|
||||
</strong>
|
||||
);
|
||||
@@ -56,7 +51,7 @@ const contentRight = (
|
||||
|
||||
storiesOf("GroupEntry", module)
|
||||
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
||||
.addDecorator((storyFn) => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.addDecorator((storyFn) => <div className="m-5">{storyFn()}</div>)
|
||||
.add("Default", () => (
|
||||
<GroupEntry link={link} avatar={icon} name={name} description={description} contentRight={contentRight} />
|
||||
))
|
||||
|
||||
@@ -21,18 +21,14 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledGroupEntry = styled.div`
|
||||
max-height: calc(90px - 1.5rem);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem;
|
||||
align-items: center;
|
||||
pointer-events: all;
|
||||
`;
|
||||
|
||||
@@ -49,7 +45,6 @@ const OverlayLink = styled(Link)`
|
||||
`;
|
||||
|
||||
const Avatar = styled.div`
|
||||
padding-right: 1rem;
|
||||
.predefined-avatar {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
@@ -57,12 +52,7 @@ const Avatar = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const Name = styled.div`
|
||||
padding: 0 0.25rem;
|
||||
`;
|
||||
|
||||
const Description = styled.p`
|
||||
padding: 0 0.25rem;
|
||||
height: 1.5rem;
|
||||
text-overflow: ellipsis;
|
||||
overflow-x: hidden;
|
||||
@@ -72,30 +62,14 @@ const Description = styled.p`
|
||||
`;
|
||||
|
||||
const ContentLeft = styled.div`
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
`;
|
||||
|
||||
const ContentRight = styled.div`
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
justify-content: flex-end;
|
||||
pointer-events: all;
|
||||
padding-left: 2rem;
|
||||
margin-bottom: -10px;
|
||||
`;
|
||||
|
||||
const NameDescriptionWrapper = styled.div`
|
||||
overflow: hidden;
|
||||
flex: 1 1 auto;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
avatar: string | ReactNode;
|
||||
@@ -107,19 +81,32 @@ type Props = {
|
||||
|
||||
const GroupEntry: FC<Props> = ({ link, avatar, title, name, description, contentRight }) => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className="is-relative">
|
||||
<OverlayLink to={link} />
|
||||
<StyledGroupEntry title={title}>
|
||||
<ContentLeft>
|
||||
<Avatar>{avatar}</Avatar>
|
||||
<NameDescriptionWrapper>
|
||||
<Name>{name}</Name>
|
||||
<Description>{description}</Description>
|
||||
</NameDescriptionWrapper>
|
||||
<StyledGroupEntry
|
||||
className={classNames("is-flex", "is-justify-content-space-between", "is-align-items-center", "p-2")}
|
||||
title={title}
|
||||
>
|
||||
<ContentLeft className={classNames("is-flex", "is-flex-grow-1", "is-align-items-center")}>
|
||||
<Avatar className="mr-4">{avatar}</Avatar>
|
||||
<div className={classNames("is-flex-grow-1", "is-clipped")}>
|
||||
<div className="mx-1">{name}</div>
|
||||
<Description className="mx-1">{description}</Description>
|
||||
</div>
|
||||
</ContentLeft>
|
||||
<ContentRight className="is-hidden-touch">{contentRight}</ContentRight>
|
||||
<ContentRight
|
||||
className={classNames(
|
||||
"is-hidden-touch",
|
||||
"is-flex",
|
||||
"is-flex-shrink-0",
|
||||
"is-justify-content-flex-end",
|
||||
"pl-5"
|
||||
)}
|
||||
>
|
||||
{contentRight}
|
||||
</ContentRight>
|
||||
</StyledGroupEntry>
|
||||
</Wrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -45,22 +45,14 @@ type Props = {
|
||||
};
|
||||
|
||||
const PageActionContainer = styled.div`
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
// every child except first
|
||||
> * ~ * {
|
||||
margin-left: 1.25rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const MarginLeft = styled.div`
|
||||
margin-left: 0.5rem;
|
||||
`;
|
||||
|
||||
const FlexContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
const MaxTitleHeight = styled.div`
|
||||
// remove blank space in repo create form
|
||||
height: 2.25rem;
|
||||
`;
|
||||
|
||||
@@ -98,12 +90,19 @@ export default class Page extends React.Component<Props> {
|
||||
|
||||
let pageActions = null;
|
||||
let pageActionsExists = false;
|
||||
React.Children.forEach(children, child => {
|
||||
React.Children.forEach(children, (child) => {
|
||||
if (child && !error) {
|
||||
if (this.isPageAction(child)) {
|
||||
pageActions = (
|
||||
<PageActionContainer
|
||||
className={classNames("column", "is-three-fifths", "is-mobile-action-spacing", "is-flex-tablet")}
|
||||
className={classNames(
|
||||
"column",
|
||||
"is-three-fifths",
|
||||
"is-mobile-action-spacing",
|
||||
"is-flex-tablet",
|
||||
"is-justify-content-flex-end",
|
||||
"is-align-items-center"
|
||||
)}
|
||||
>
|
||||
{child}
|
||||
</PageActionContainer>
|
||||
@@ -119,10 +118,10 @@ export default class Page extends React.Component<Props> {
|
||||
<>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<FlexContainer>
|
||||
<MaxTitleHeight className="is-flex">
|
||||
<Title title={this.getTextualTitle()}>{this.getTitleComponent()}</Title>
|
||||
{afterTitle && <MarginLeft>{afterTitle}</MarginLeft>}
|
||||
</FlexContainer>
|
||||
{afterTitle && <div className="ml-2">{afterTitle}</div>}
|
||||
</MaxTitleHeight>
|
||||
{subtitle ? <Subtitle>{subtitle}</Subtitle> : null}
|
||||
</div>
|
||||
{pageActions}
|
||||
@@ -145,7 +144,7 @@ export default class Page extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const content: ReactNode[] = [];
|
||||
React.Children.forEach(children, child => {
|
||||
React.Children.forEach(children, (child) => {
|
||||
if (child) {
|
||||
if (!this.isPageAction(child)) {
|
||||
content.push(child);
|
||||
|
||||
@@ -76,7 +76,7 @@ export const Modal: FC<Props> = ({
|
||||
<div className="modal-background" onClick={closeFunction} />
|
||||
<SizedModal className="modal-card" size={size}>
|
||||
<header className={classNames("modal-card-head", `has-background-${headColor}`)}>
|
||||
<p className={`modal-card-title is-marginless has-text-${headTextColor}`}>{title}</p>
|
||||
<p className={`modal-card-title m-0 has-text-${headTextColor}`}>{title}</p>
|
||||
<button className="delete" aria-label="close" onClick={closeFunction} />
|
||||
</header>
|
||||
<section className="modal-card-body">{body}</section>
|
||||
|
||||
@@ -82,7 +82,7 @@ const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true })
|
||||
<SectionContainer className="menu">
|
||||
<div>
|
||||
<MenuLabel
|
||||
className={classNames("menu-label", { "has-cursor-pointer": collapsible })}
|
||||
className={classNames("menu-label", { "is-clickable": collapsible })}
|
||||
collapsed={isCollapsed}
|
||||
onClick={toggleCollapseState}
|
||||
>
|
||||
|
||||
@@ -43,7 +43,7 @@ type ContainerProps = {
|
||||
const PopoverContainer = styled.div<ContainerProps>`
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
width: ${props => props.width}px;
|
||||
width: ${(props) => props.width}px;
|
||||
display: block;
|
||||
|
||||
&:before {
|
||||
@@ -54,7 +54,7 @@ const PopoverContainer = styled.div<ContainerProps>`
|
||||
height: 0;
|
||||
width: 0;
|
||||
top: 100%;
|
||||
left: ${props => props.width / 2}px;
|
||||
left: ${(props) => props.width / 2}px;
|
||||
border-color: transparent;
|
||||
border-bottom-color: white;
|
||||
border-left-color: white;
|
||||
@@ -67,15 +67,11 @@ const PopoverContainer = styled.div<ContainerProps>`
|
||||
}
|
||||
`;
|
||||
|
||||
const SmallHr = styled.hr`
|
||||
margin: 0.5em 0;
|
||||
`;
|
||||
|
||||
const PopoverHeading = styled.div`
|
||||
height: 1.5em;
|
||||
`;
|
||||
|
||||
const Popover: FC<Props> = props => {
|
||||
const Popover: FC<Props> = (props) => {
|
||||
if (!props.show) {
|
||||
return null;
|
||||
}
|
||||
@@ -93,13 +89,13 @@ const InnerPopover: FC<Props> = ({ title, show, width, offsetTop, offsetLeft, di
|
||||
|
||||
const onMouseEnter = () => {
|
||||
dispatch({
|
||||
type: "enter-popover"
|
||||
type: "enter-popover",
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
dispatch({
|
||||
type: "leave-popover"
|
||||
type: "leave-popover",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -109,20 +105,20 @@ const InnerPopover: FC<Props> = ({ title, show, width, offsetTop, offsetLeft, di
|
||||
<PopoverContainer
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
className={"box"}
|
||||
className="box"
|
||||
style={{ top: `${top}px`, left: `${left}px` }}
|
||||
width={width!}
|
||||
ref={ref}
|
||||
>
|
||||
<PopoverHeading>{title}</PopoverHeading>
|
||||
<SmallHr />
|
||||
<hr className="my-2" />
|
||||
{children}
|
||||
</PopoverContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Popover.defaultProps = {
|
||||
width: 120
|
||||
width: 120,
|
||||
};
|
||||
|
||||
export default Popover;
|
||||
|
||||
@@ -27,7 +27,6 @@ import Tooltip from "../Tooltip";
|
||||
|
||||
const Button = styled.a`
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #33b2e8;
|
||||
}
|
||||
@@ -47,7 +46,7 @@ const DiffButton: FC<Props> = ({ icon, tooltip, onClick }) => {
|
||||
|
||||
return (
|
||||
<Tooltip message={tooltip} location="top">
|
||||
<Button aria-label={tooltip} className="button" onClick={handleClick}>
|
||||
<Button aria-label={tooltip} className="button is-clickable" onClick={handleClick}>
|
||||
<i className={`fas fa-${icon}`} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -69,22 +69,6 @@ const FullWidthTitleHeader = styled.div`
|
||||
max-width: 100%;
|
||||
`;
|
||||
|
||||
const TitleWrapper = styled.span`
|
||||
margin-left: 0.25rem;
|
||||
`;
|
||||
|
||||
const AlignRight = styled.div`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
const HunkDivider = styled.hr`
|
||||
margin: 0.5rem 0;
|
||||
`;
|
||||
|
||||
const ChangeTypeTag = styled(Tag)`
|
||||
margin-left: 0.75rem;
|
||||
`;
|
||||
|
||||
const MarginlessModalContent = styled.div`
|
||||
margin: -1.25rem;
|
||||
|
||||
@@ -319,7 +303,7 @@ class DiffFile extends React.Component<Props, State> {
|
||||
} else if (i > 0) {
|
||||
items.push(
|
||||
<Decoration>
|
||||
<HunkDivider />
|
||||
<hr className="my-2" />
|
||||
</Decoration>
|
||||
);
|
||||
}
|
||||
@@ -403,8 +387,8 @@ class DiffFile extends React.Component<Props, State> {
|
||||
|
||||
const color = value === "added" ? "success" : value === "deleted" ? "danger" : "info";
|
||||
return (
|
||||
<ChangeTypeTag
|
||||
className={classNames("has-text-weight-normal")}
|
||||
<Tag
|
||||
className={classNames("has-text-weight-normal", "ml-3")}
|
||||
rounded={true}
|
||||
outlined={true}
|
||||
color={color}
|
||||
@@ -431,7 +415,7 @@ class DiffFile extends React.Component<Props, State> {
|
||||
|
||||
const fileAnnotations = fileAnnotationFactory ? fileAnnotationFactory(file) : null;
|
||||
const innerContent = (
|
||||
<div className="panel-block is-paddingless">
|
||||
<div className="panel-block p-0">
|
||||
{fileAnnotations}
|
||||
<TokenizedDiffView className={viewType} viewType={viewType} file={file}>
|
||||
{(hunks: HunkType[]) =>
|
||||
@@ -475,13 +459,13 @@ class DiffFile extends React.Component<Props, State> {
|
||||
</MenuContext.Consumer>
|
||||
);
|
||||
const headerButtons = (
|
||||
<AlignRight className={classNames("level-right", "is-flex")}>
|
||||
<div className={classNames("level-right", "is-flex", "ml-auto")}>
|
||||
<ButtonGroup>
|
||||
{sideBySideToggle}
|
||||
{openInFullscreen}
|
||||
{fileControls}
|
||||
</ButtonGroup>
|
||||
</AlignRight>
|
||||
</div>
|
||||
);
|
||||
|
||||
let errorModal;
|
||||
@@ -506,14 +490,14 @@ class DiffFile extends React.Component<Props, State> {
|
||||
<div className="panel-heading">
|
||||
<div className={classNames("level", "is-flex-wrap-wrap")}>
|
||||
<FullWidthTitleHeader
|
||||
className={classNames("level-left", "is-flex", "has-cursor-pointer")}
|
||||
className={classNames("level-left", "is-flex", "is-clickable")}
|
||||
onClick={this.toggleCollapse}
|
||||
title={this.hoverFileTitle(file)}
|
||||
>
|
||||
{collapseIcon}
|
||||
<TitleWrapper className={classNames("is-ellipsis-overflow", "is-size-6")}>
|
||||
<span className={classNames("is-ellipsis-overflow", "is-size-6", "ml-1")}>
|
||||
{this.renderFileTitle(file)}
|
||||
</TitleWrapper>
|
||||
</span>
|
||||
{this.renderChangeTag(file)}
|
||||
</FullWidthTitleHeader>
|
||||
{headerButtons}
|
||||
|
||||
@@ -43,7 +43,7 @@ const HealthCheckFailureDetail: FC<Props> = ({ active, closeFunction, failures }
|
||||
return (
|
||||
<Modal
|
||||
body={
|
||||
<div className={"content"}>
|
||||
<div className="content">
|
||||
<HealthCheckFailureList failures={failures} />
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -28,14 +28,12 @@ import styled from "styled-components";
|
||||
|
||||
const HunkDivider = styled.div`
|
||||
background: #98d8f3;
|
||||
font-size: 0.7rem;
|
||||
padding-left: 1.78em;
|
||||
`;
|
||||
|
||||
const HunkExpandDivider: FC = ({ children }) => {
|
||||
return (
|
||||
<Decoration>
|
||||
<HunkDivider>{children}</HunkDivider>
|
||||
<HunkDivider className="is-size-7 pl-5">{children}</HunkDivider>
|
||||
</Decoration>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
import React, { FC, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
icon: string;
|
||||
@@ -32,10 +31,6 @@ type Props = {
|
||||
onClick: () => Promise<any>;
|
||||
};
|
||||
|
||||
const ExpandLink = styled.span`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const HunkExpandLink: FC<Props> = ({ icon, text, onClick }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -49,9 +44,9 @@ const HunkExpandLink: FC<Props> = ({ icon, text, onClick }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ExpandLink onClick={onClickWithLoadingMarker}>
|
||||
<span className="is-clickable" onClick={onClickWithLoadingMarker}>
|
||||
<i className={classNames("fa", icon)} /> {loading ? t("diff.expanding") : text}
|
||||
</ExpandLink>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -24,13 +24,11 @@
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Tooltip from "../Tooltip";
|
||||
import Icon from "../Icon";
|
||||
|
||||
const Button = styled(Link)`
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #33b2e8;
|
||||
}
|
||||
@@ -44,7 +42,7 @@ type Props = {
|
||||
const JumpToFileButton: FC<Props> = ({ link, tooltip }) => {
|
||||
return (
|
||||
<Tooltip message={tooltip} location="top">
|
||||
<Button aria-label={tooltip} className="button" to={link}>
|
||||
<Button aria-label={tooltip} className="button is-clickable" to={link}>
|
||||
<Icon name="file-code" color="inherit" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -22,16 +22,14 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { NotFoundError, useDiff } from "@scm-manager/ui-api";
|
||||
import ErrorNotification from "../ErrorNotification";
|
||||
import Notification from "../Notification";
|
||||
|
||||
import Loading from "../Loading";
|
||||
import Notification from "../Notification";
|
||||
import Button from "../buttons/Button";
|
||||
import Diff from "./Diff";
|
||||
import { DiffObjectProps } from "./DiffTypes";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Button from "../buttons/Button";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = DiffObjectProps & {
|
||||
url: string;
|
||||
@@ -44,19 +42,15 @@ type NotificationProps = {
|
||||
isFetchingNextPage: boolean;
|
||||
};
|
||||
|
||||
const StyledNotification = styled(Notification)`
|
||||
margin-top: 1.5rem;
|
||||
`;
|
||||
|
||||
const PartialNotification: FC<NotificationProps> = ({ fetchNextPage, isFetchingNextPage }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
return (
|
||||
<StyledNotification type="info">
|
||||
<Notification className="mt-5" type="info">
|
||||
<div className="columns is-centered">
|
||||
<div className="column">{t("changesets.moreDiffsAvailable")}</div>
|
||||
<Button label={t("changesets.loadMore")} action={fetchNextPage} loading={isFetchingNextPage} />
|
||||
</div>
|
||||
</StyledNotification>
|
||||
</Notification>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import RepositoryFlags from "./RepositoryFlags";
|
||||
import styled from "styled-components";
|
||||
import Icon from "../Icon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
|
||||
type DateProp = Date | string;
|
||||
|
||||
@@ -43,26 +44,9 @@ type Props = {
|
||||
|
||||
const ContentRightContainer = styled.div`
|
||||
height: calc(80px - 1.5rem);
|
||||
margin-right: 1rem;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const DateWrapper = styled.small`
|
||||
padding-bottom: 0.25rem;
|
||||
`;
|
||||
|
||||
const QuickActionbar = styled.span`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
`;
|
||||
|
||||
const QuickAction = styled(Icon)`
|
||||
font-size: 1.25rem;
|
||||
|
||||
:hover {
|
||||
color: #363636 !important;
|
||||
}
|
||||
@@ -80,7 +64,15 @@ const RepositoryEntry: FC<Props> = ({ repository, baseDate }) => {
|
||||
const [openCloneModal, setOpenCloneModal] = useState(false);
|
||||
|
||||
const createContentRight = () => (
|
||||
<ContentRightContainer>
|
||||
<ContentRightContainer
|
||||
className={classNames(
|
||||
"is-flex",
|
||||
"is-flex-direction-column",
|
||||
"is-justify-content-space-between",
|
||||
"is-relative",
|
||||
"mr-4"
|
||||
)}
|
||||
>
|
||||
{openCloneModal && (
|
||||
<Modal
|
||||
size="L"
|
||||
@@ -98,18 +90,18 @@ const RepositoryEntry: FC<Props> = ({ repository, baseDate }) => {
|
||||
closeFunction={() => setOpenCloneModal(false)}
|
||||
/>
|
||||
)}
|
||||
<QuickActionbar>
|
||||
<span className={classNames("is-flex", "is-justify-content-flex-end", "is-align-items-flex-end")}>
|
||||
<QuickAction
|
||||
className={classNames("is-clickable", "is-size-5")}
|
||||
name="download"
|
||||
color="info"
|
||||
className="has-cursor-pointer"
|
||||
onClick={() => setOpenCloneModal(true)}
|
||||
title={t("overview.clone")}
|
||||
/>
|
||||
</QuickActionbar>
|
||||
<DateWrapper>
|
||||
</span>
|
||||
<small className="pb-1">
|
||||
<DateFromNow baseDate={baseDate} date={repository.lastModified || repository.creationDate} />
|
||||
</DateWrapper>
|
||||
</small>
|
||||
</ContentRightContainer>
|
||||
);
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { Icon } from "@scm-manager/ui-components";
|
||||
import Tooltip from "../Tooltip";
|
||||
|
||||
@@ -33,10 +32,6 @@ type Props = {
|
||||
tooltip?: string;
|
||||
};
|
||||
|
||||
const PointerEventsLink = styled(Link)`
|
||||
pointer-events: all;
|
||||
`;
|
||||
|
||||
class RepositoryEntryLink extends React.Component<Props> {
|
||||
render() {
|
||||
const { to, icon, tooltip } = this.props;
|
||||
@@ -51,9 +46,9 @@ class RepositoryEntryLink extends React.Component<Props> {
|
||||
}
|
||||
|
||||
return (
|
||||
<PointerEventsLink className="level-item" to={to}>
|
||||
<Link className="level-item is-clickable" to={to}>
|
||||
{content}
|
||||
</PointerEventsLink>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC, useState } from "react";
|
||||
import RepositoryFlag from "./RepositoryFlag";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import HealthCheckFailureDetail from "./HealthCheckFailureDetail";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { TooltipLocation } from "../Tooltip";
|
||||
import RepositoryFlag from "./RepositoryFlag";
|
||||
import HealthCheckFailureDetail from "./HealthCheckFailureDetail";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
@@ -37,11 +37,6 @@ type Props = {
|
||||
tooltipLocation?: TooltipLocation;
|
||||
};
|
||||
|
||||
const Wrapper = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const RepositoryFlagContainer = styled.div`
|
||||
.tag {
|
||||
margin-left: 0.25rem;
|
||||
@@ -91,13 +86,13 @@ const RepositoryFlags: FC<Props> = ({ repository, className, tooltipLocation = "
|
||||
);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<span className={classNames("is-flex", "is-align-items-center", className)}>
|
||||
{modal}
|
||||
<RepositoryFlagContainer>
|
||||
{repositoryFlags}
|
||||
<ExtensionPoint name="repository.flags" props={{ repository, tooltipLocation }} renderAll={true} />
|
||||
</RepositoryFlagContainer>
|
||||
</Wrapper>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import Popover from "./Popover";
|
||||
import AnnotateLine from "./AnnotateLine";
|
||||
import { Action } from "./actions";
|
||||
import { determineLanguage } from "../../languages";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
source: AnnotatedSource;
|
||||
@@ -57,11 +56,6 @@ const initialState = {
|
||||
onLine: false,
|
||||
};
|
||||
|
||||
const NoSpacingReactSyntaxHighlighter = styled(ReactSyntaxHighlighter)`
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
`;
|
||||
|
||||
const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case "enter-line": {
|
||||
@@ -152,14 +146,15 @@ const Annotate: FC<Props> = ({ source, repository, baseDate }) => {
|
||||
return (
|
||||
<div className="panel-block">
|
||||
{popover}
|
||||
<NoSpacingReactSyntaxHighlighter
|
||||
<ReactSyntaxHighlighter
|
||||
className="m-0 p-0"
|
||||
showLineNumbers={false}
|
||||
language={determineLanguage(source.language)}
|
||||
style={highlightingTheme}
|
||||
renderer={defaultRenderer}
|
||||
>
|
||||
{code}
|
||||
</NoSpacingReactSyntaxHighlighter>
|
||||
</ReactSyntaxHighlighter>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,8 +44,6 @@ const Author = styled(LineElement)`
|
||||
`;
|
||||
|
||||
const When = styled(LineElement)`
|
||||
display: inline-block;
|
||||
|
||||
width: 6.5em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -31,7 +31,7 @@ import { DateInput } from "../../useDateFormatter";
|
||||
import { Repository, AnnotatedLine } from "@scm-manager/ui-types";
|
||||
import AuthorImage from "./AuthorImage";
|
||||
import { Action } from "./actions";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const PopoverContainer = styled.div`
|
||||
position: absolute;
|
||||
@@ -62,16 +62,11 @@ const PopoverContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const SmallHr = styled.hr`
|
||||
margin: 0.5em 0;
|
||||
`;
|
||||
|
||||
const PopoverHeading = styled.div`
|
||||
height: 1.5em;
|
||||
`;
|
||||
|
||||
const PopoverDescription = styled.p`
|
||||
margin-top: 0.5em;
|
||||
overflow-wrap: break-word;
|
||||
`;
|
||||
|
||||
@@ -102,13 +97,13 @@ const Popover: FC<PopoverProps> = ({ annotation, offsetTop, repository, baseDate
|
||||
|
||||
const onMouseEnter = () => {
|
||||
dispatch({
|
||||
type: "enter-popover"
|
||||
type: "enter-popover",
|
||||
});
|
||||
};
|
||||
|
||||
const OnMouseLeave = () => {
|
||||
dispatch({
|
||||
type: "leave-popover"
|
||||
type: "leave-popover",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -128,14 +123,14 @@ const Popover: FC<PopoverProps> = ({ annotation, offsetTop, repository, baseDate
|
||||
</span>
|
||||
<DateFromNow className="is-pulled-right" date={annotation.when} baseDate={baseDate} />
|
||||
</PopoverHeading>
|
||||
<SmallHr />
|
||||
<hr className="my-2" />
|
||||
<p>
|
||||
{t("changeset.label") + " "}
|
||||
<Link to={`/repo/${repository.namespace}/${repository.name}/code/changeset/${annotation.revision}`}>
|
||||
{shortRevision(annotation.revision)}
|
||||
</Link>
|
||||
</p>
|
||||
<PopoverDescription className="content">{annotation.description}</PopoverDescription>
|
||||
<PopoverDescription className="content mt-2">{annotation.description}</PopoverDescription>
|
||||
</PopoverContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@ class ChangesetButtonGroup extends React.Component<Props> {
|
||||
const changesetLink = createChangesetLink(repository, changeset);
|
||||
const sourcesLink = createSourcesLink(repository, changeset);
|
||||
return (
|
||||
<ButtonAddons className="is-marginless">
|
||||
<ButtonAddons className="m-0">
|
||||
<Button link={changesetLink} icon="exchange-alt" label={t("changeset.buttons.details")} reducedMobile={true} />
|
||||
<Button link={sourcesLink} icon="code" label={t("changeset.buttons.sources")} reducedMobile={true} />
|
||||
</ButtonAddons>
|
||||
|
||||
@@ -52,39 +52,15 @@ const Wrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const AvatarFigure = styled.figure`
|
||||
margin-top: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
`;
|
||||
|
||||
const FixedSizedAvatar = styled.div`
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
`;
|
||||
|
||||
const Metadata = styled.div`
|
||||
margin-left: 0;
|
||||
const FullWidthDiv = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const AuthorWrapper = styled.p`
|
||||
margin-top: 0.5rem;
|
||||
`;
|
||||
|
||||
const VCenteredColumn = styled.div`
|
||||
align-self: center;
|
||||
`;
|
||||
|
||||
const VCenteredChildColumn = styled.div`
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
const FlexRow = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
class ChangesetRow extends React.Component<Props> {
|
||||
createChangesetId = (changeset: Changeset) => {
|
||||
const { repository } = this.props;
|
||||
@@ -99,25 +75,25 @@ class ChangesetRow extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className="columns is-gapless is-mobile">
|
||||
<div className="column is-three-fifths">
|
||||
<div className="columns is-gapless">
|
||||
<div className="column is-four-fifths">
|
||||
<div className={classNames("columns", "is-gapless", "is-mobile")}>
|
||||
<div className={classNames("column", "is-three-fifths")}>
|
||||
<div className={classNames("columns", "is-gapless")}>
|
||||
<div className={classNames("column", "is-four-fifths")}>
|
||||
<div className="media">
|
||||
<AvatarWrapper>
|
||||
<AvatarFigure className="media-left">
|
||||
<figure className={classNames("media-left", "mt-2", "mr-2")}>
|
||||
<FixedSizedAvatar className="image">
|
||||
<AvatarImage person={changeset.author} />
|
||||
</FixedSizedAvatar>
|
||||
</AvatarFigure>
|
||||
</figure>
|
||||
</AvatarWrapper>
|
||||
<Metadata className="media-right">
|
||||
<h4 className="has-text-weight-bold is-ellipsis-overflow">
|
||||
<FullWidthDiv className={classNames("media-right", "ml-0")}>
|
||||
<h4 className={classNames("has-text-weight-bold", "is-ellipsis-overflow")}>
|
||||
<ExtensionPoint
|
||||
name="changeset.description"
|
||||
props={{
|
||||
changeset,
|
||||
value: description.title
|
||||
value: description.title,
|
||||
}}
|
||||
renderAll={false}
|
||||
>
|
||||
@@ -130,36 +106,33 @@ class ChangesetRow extends React.Component<Props> {
|
||||
<p className="is-hidden-desktop">
|
||||
<Trans i18nKey="repos:changeset.shortSummary" components={[changesetId, dateFromNow]} />
|
||||
</p>
|
||||
<FlexRow>
|
||||
<AuthorWrapper className="is-size-7 is-ellipsis-overflow">
|
||||
<div className="is-flex">
|
||||
<p className={classNames("is-size-7", "is-ellipsis-overflow", "mt-2")}>
|
||||
<ChangesetAuthor changeset={changeset} />
|
||||
</AuthorWrapper>
|
||||
</p>
|
||||
{changeset?.signatures && changeset.signatures.length > 0 && (
|
||||
<SignatureIcon
|
||||
className="mx-2 pt-1"
|
||||
signatures={changeset.signatures}
|
||||
/>
|
||||
<SignatureIcon className={classNames("mx-2", "pt-1")} signatures={changeset.signatures} />
|
||||
)}
|
||||
</FlexRow>
|
||||
</Metadata>
|
||||
</div>
|
||||
</FullWidthDiv>
|
||||
</div>
|
||||
</div>
|
||||
<VCenteredColumn className="column">
|
||||
<div className={classNames("column", "is-align-self-center")}>
|
||||
<ChangesetTags changeset={changeset} />
|
||||
</VCenteredColumn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<VCenteredChildColumn className={classNames("column", "is-flex")}>
|
||||
<div className={classNames("column", "is-flex", "is-justify-content-flex-end", "is-align-items-center")}>
|
||||
<ChangesetButtonGroup repository={repository} changeset={changeset} />
|
||||
<ExtensionPoint
|
||||
name="changeset.right"
|
||||
props={{
|
||||
repository,
|
||||
changeset
|
||||
changeset,
|
||||
}}
|
||||
renderAll={true}
|
||||
/>
|
||||
</VCenteredChildColumn>
|
||||
</div>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import classNames from "classnames";
|
||||
import Icon from "../Icon";
|
||||
|
||||
type Props = {
|
||||
@@ -30,13 +30,8 @@ type Props = {
|
||||
isVisible: boolean;
|
||||
};
|
||||
|
||||
const IconWithMarginLeft = styled(Icon)`
|
||||
visibility: ${(props: Props) => (props.isVisible ? "visible" : "hidden")};
|
||||
margin-left: 0.25em;
|
||||
`;
|
||||
|
||||
const SortIcon: FC<Props> = (props: Props) => {
|
||||
return <IconWithMarginLeft name={props.name} isVisible={props.isVisible} />;
|
||||
return <Icon className={classNames("ml-1", { "is-invisible": !props.isVisible })} name={props.name} />;
|
||||
};
|
||||
|
||||
export default SortIcon;
|
||||
|
||||
@@ -109,7 +109,7 @@ const Table: FC<Props> = ({ data, sortable, children, emptyMessage, className })
|
||||
<tr>
|
||||
{React.Children.map(children, (child, index) => (
|
||||
<th
|
||||
className={isSortable(child) && "has-cursor-pointer"}
|
||||
className={isSortable(child) && "is-clickable"}
|
||||
onClick={isSortable(child) ? () => tableSort(index) : undefined}
|
||||
onMouseEnter={() => setHoveredColumnIndex(index)}
|
||||
onMouseLeave={() => setHoveredColumnIndex(undefined)}
|
||||
|
||||
@@ -59,11 +59,6 @@ const Container = styled.div<Themeable>`
|
||||
}
|
||||
`;
|
||||
|
||||
const Title = styled.h1<Themeable>`
|
||||
margin-bottom: 0.25rem;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const Toast: FC<Props> = ({ children, title, type }) => {
|
||||
const rootElement = usePortalRootElement("toastRoot");
|
||||
if (!rootElement) {
|
||||
@@ -74,7 +69,7 @@ const Toast: FC<Props> = ({ children, title, type }) => {
|
||||
const theme = getTheme(type);
|
||||
const content = (
|
||||
<Container theme={theme}>
|
||||
<Title theme={theme}>{title}</Title>
|
||||
<h1 className="mb-1 has-text-weight-bold">{title}</h1>
|
||||
<ToastThemeContext.Provider value={theme}>{children}</ToastThemeContext.Provider>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
import React, { FC, useContext, MouseEvent } from "react";
|
||||
import { ToastThemeContext, Themeable } from "./themes";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
@@ -30,25 +31,21 @@ type Props = {
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const ThemedButton = styled.button.attrs(props => ({
|
||||
className: "button"
|
||||
const ThemedButton = styled.button.attrs((props) => ({
|
||||
className: "button",
|
||||
}))<Themeable>`
|
||||
color: ${props => props.theme.primary};
|
||||
border-color: ${props => props.theme.primary};
|
||||
background-color: ${props => props.theme.secondary};
|
||||
color: ${(props) => props.theme.primary};
|
||||
border-color: ${(props) => props.theme.primary};
|
||||
background-color: ${(props) => props.theme.secondary};
|
||||
font-size: 0.75rem;
|
||||
|
||||
&:hover {
|
||||
color: ${props => props.theme.primary};
|
||||
border-color: ${props => props.theme.tertiary};
|
||||
background-color: ${props => props.theme.tertiary};
|
||||
color: ${(props) => props.theme.primary};
|
||||
border-color: ${(props) => props.theme.tertiary};
|
||||
background-color: ${(props) => props.theme.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
const ToastButtonIcon = styled.i`
|
||||
margin-right: 0.25rem;
|
||||
`;
|
||||
|
||||
const ToastButton: FC<Props> = ({ icon, onClick, children }) => {
|
||||
const theme = useContext(ToastThemeContext);
|
||||
|
||||
@@ -61,7 +58,7 @@ const ToastButton: FC<Props> = ({ icon, onClick, children }) => {
|
||||
|
||||
return (
|
||||
<ThemedButton theme={theme} onClick={handleClick}>
|
||||
{icon && <ToastButtonIcon className={`fas fa-fw fa-${icon}`} />} {children}
|
||||
{icon && <i className={classNames("fas", "fa-fw", `fa-${icon}`, "mr-1")} />} {children}
|
||||
</ThemedButton>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,9 +24,7 @@
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Buttons = styled.div`
|
||||
display: flex;
|
||||
padding-top: 0.5rem;
|
||||
const FullWidthDiv = styled.div`
|
||||
width: 100%;
|
||||
|
||||
& > * {
|
||||
@@ -38,6 +36,6 @@ const Buttons = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const ToastButtons: FC = ({ children }) => <Buttons>{children}</Buttons>;
|
||||
const ToastButtons: FC = ({ children }) => <FullWidthDiv className="is-flex pt-2">{children}</FullWidthDiv>;
|
||||
|
||||
export default ToastButtons;
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { getTheme, Themeable, ToastThemeContext, Type } from "./themes";
|
||||
import styled from "styled-components";
|
||||
import React, { FC } from "react";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { getTheme, Themeable, ToastThemeContext, Type } from "./themes";
|
||||
|
||||
type Props = {
|
||||
type: Type;
|
||||
@@ -33,31 +33,22 @@ type Props = {
|
||||
};
|
||||
|
||||
const Container = styled.div<Themeable>`
|
||||
color: ${props => props.theme.primary};
|
||||
background-color: ${props => props.theme.secondary};
|
||||
color: ${(props) => props.theme.primary};
|
||||
background-color: ${(props) => props.theme.secondary};
|
||||
max-width: 18rem;
|
||||
font-size: 0.75rem;
|
||||
border-radius: 5px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0 !important;
|
||||
|
||||
& > p {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const Title = styled.h1<Themeable>`
|
||||
margin-bottom: 0.25rem;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
const ToastNotification: FC<Props> = ({ children, title, type, close }) => {
|
||||
const theme = getTheme(type);
|
||||
return (
|
||||
<Container className="notification" theme={theme}>
|
||||
{ close ? <button className="delete" onClick={close} /> : null }
|
||||
<Title theme={theme}>{title}</Title>
|
||||
<Container className={classNames("notification", "mt-2", "mb-0", "p-5", "is-size-7")} theme={theme}>
|
||||
{close ? <button className="delete" onClick={close} /> : null}
|
||||
<h1 className={classNames("mb-1", "has-text-weight-bold")}>{title}</h1>
|
||||
<ToastThemeContext.Provider value={theme}>{children}</ToastThemeContext.Provider>
|
||||
</Container>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user