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:
Florian Scholdei
2021-09-15 17:40:08 +02:00
committed by GitHub
parent 8a65660278
commit 2cb006d040
97 changed files with 1931 additions and 2244 deletions

View File

@@ -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>
);

View File

@@ -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}
/>
));

View File

@@ -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" />
</>
);
};

View File

@@ -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>
</>
);

View File

@@ -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>
);
}
}

View File

@@ -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)

View File

@@ -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>
);

View File

@@ -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>
));

View File

@@ -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,
});
}

View File

@@ -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>
</>
));

View File

@@ -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;

View File

@@ -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>
);

View File

@@ -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)}

View File

@@ -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>
</>
);
});

View File

@@ -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

View File

@@ -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>
);

View File

@@ -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>
}
/>
);

View File

@@ -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>
));

View File

@@ -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>
);
};

View File

@@ -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}

View File

@@ -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>
);
};

View File

@@ -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>
);

View File

@@ -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" />
</>
);

View File

@@ -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} />
))

View File

@@ -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>
);
};

View File

@@ -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);

View File

@@ -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>

View File

@@ -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}
>

View File

@@ -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;

View File

@@ -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>

View File

@@ -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}

View File

@@ -43,7 +43,7 @@ const HealthCheckFailureDetail: FC<Props> = ({ active, closeFunction, failures }
return (
<Modal
body={
<div className={"content"}>
<div className="content">
<HealthCheckFailureList failures={failures} />
</div>
}

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

@@ -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>

View File

@@ -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>
);
};

View File

@@ -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>
);

View File

@@ -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>
);
}
}

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

@@ -44,8 +44,6 @@ const Author = styled(LineElement)`
`;
const When = styled(LineElement)`
display: inline-block;
width: 6.5em;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -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>
);
};

View File

@@ -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>

View File

@@ -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>
);

View File

@@ -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;

View File

@@ -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)}

View File

@@ -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>
);

View File

@@ -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>
);
};

View File

@@ -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;

View File

@@ -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>
);