mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 17:05:43 +01:00
Improve card (list) component and its fix layout issues
Committed-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
@@ -27,12 +27,11 @@ import { ComponentMeta, StoryFn } from "@storybook/react";
|
||||
import React, { ComponentProps } from "react";
|
||||
import { ExtractProps } from "@scm-manager/ui-extensions";
|
||||
import { Link } from "react-router-dom";
|
||||
import CardList, { CardListBox } from "./CardList";
|
||||
import Card from "./Card";
|
||||
import CardTitle from "./CardTitle";
|
||||
import CardList, { CardListBox, CardListCard } from "./CardList";
|
||||
import CardTitle from "../card/CardTitle";
|
||||
import { Menu } from "@scm-manager/ui-overlays";
|
||||
import { Icon } from "@scm-manager/ui-buttons";
|
||||
import CardRow from "./CardRow";
|
||||
import CardRow from "../card/CardRow";
|
||||
|
||||
export default {
|
||||
title: "CardList",
|
||||
@@ -46,22 +45,50 @@ export const Default = Template.bind({});
|
||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||
Default.args = {
|
||||
children: [
|
||||
// eslint-disable-next-line no-console
|
||||
<Card>
|
||||
<CardRow>
|
||||
<CardTitle>My favorite repository</CardTitle>
|
||||
</CardRow>
|
||||
</Card>,
|
||||
<Card>
|
||||
<CardRow>
|
||||
<CardTitle>
|
||||
<Link aria-label="Edit My least liked repo" to="/cards/1">
|
||||
My least liked repo
|
||||
</Link>
|
||||
</CardTitle>
|
||||
</CardRow>
|
||||
</Card>,
|
||||
<Card
|
||||
<CardListCard>
|
||||
<CardTitle>My favorite repository</CardTitle>
|
||||
</CardListCard>,
|
||||
<CardListCard
|
||||
action={
|
||||
<Menu>
|
||||
<Menu.Button>
|
||||
<Icon>trash</Icon>
|
||||
Delete
|
||||
</Menu.Button>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
|
||||
kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur
|
||||
sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
|
||||
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata
|
||||
sanctus est Lorem ipsum dolor sit amet.
|
||||
</CardListCard>,
|
||||
<CardListCard
|
||||
action={
|
||||
<Menu>
|
||||
<Menu.Button>
|
||||
<Icon>trash</Icon>
|
||||
Delete
|
||||
</Menu.Button>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<CardTitle level={5}>
|
||||
<Link aria-label="Edit My least liked repo" to="/cards/1">
|
||||
My favorite repository with a very long title and some other things
|
||||
</Link>
|
||||
</CardTitle>
|
||||
</CardListCard>,
|
||||
<CardListCard>
|
||||
<CardTitle>
|
||||
<Link aria-label="Edit My least liked repo" to="/cards/1">
|
||||
My least liked repo
|
||||
</Link>
|
||||
</CardTitle>
|
||||
</CardListCard>,
|
||||
<CardListCard
|
||||
action={
|
||||
<Menu>
|
||||
<Menu.Button>
|
||||
@@ -86,8 +113,8 @@ Default.args = {
|
||||
<CardRow className="is-size-6 is-flex is-justify-content-space-between">
|
||||
<span>This is a third row, lets see how this works out.</span>(MERGED)
|
||||
</CardRow>
|
||||
</Card>,
|
||||
<Card>
|
||||
</CardListCard>,
|
||||
<CardListCard>
|
||||
<CardRow className="is-flex is-align-items-center">
|
||||
<CardTitle>
|
||||
<Link
|
||||
@@ -112,6 +139,6 @@ Default.args = {
|
||||
</div>
|
||||
<span>(OPEN)</span>
|
||||
</CardRow>
|
||||
</Card>,
|
||||
</CardListCard>,
|
||||
],
|
||||
} as ComponentProps<typeof CardListBox>;
|
||||
|
||||
@@ -22,18 +22,30 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { HTMLAttributes } from "react";
|
||||
import React, { ComponentProps, HTMLAttributes } from "react";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import Card from "../card/Card";
|
||||
|
||||
/**
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
export const CardListCard = React.forwardRef<HTMLElement, Omit<ComponentProps<typeof Card>, "as">>((props, ref) => (
|
||||
<Card ref={ref} {...props} as="li" />
|
||||
));
|
||||
|
||||
const CardListElement = styled.ul`
|
||||
> * + * {
|
||||
border-top: var(--scm-border);
|
||||
margin-top: 0.5rem;
|
||||
padding-top: 1rem !important;
|
||||
margin-top: calc(0.5rem + 1px);
|
||||
|
||||
*:is(h1, h2, h3, h4, h5, h6) a::after {
|
||||
top: 0.5rem !important;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
border-top: var(--scm-border);
|
||||
left: 0;
|
||||
top: calc(-0.25rem - 1px);
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -41,8 +53,6 @@ const CardListElement = styled.ul`
|
||||
type Props = HTMLAttributes<HTMLUListElement>;
|
||||
|
||||
/**
|
||||
* The {@link CardList.Card.Title} is currently represented as a `h3`, which means the list can only be used on the top level of the page without breaking accessibility.
|
||||
*
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
@@ -53,8 +63,6 @@ const CardList = React.forwardRef<HTMLUListElement, Props>(({ children, classNam
|
||||
));
|
||||
|
||||
/**
|
||||
* The {@link CardList.Card.Title} is currently represented as a `h3`, which means the list can only be used on the top level of the page without breaking accessibility.
|
||||
*
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
|
||||
@@ -22,36 +22,39 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { LiHTMLAttributes } from "react";
|
||||
import React, { ComponentType, HTMLAttributes, ReactHTML, Ref } from "react";
|
||||
import styled from "styled-components";
|
||||
import classNames from "classnames";
|
||||
|
||||
const CardElement = styled.li`
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) min-content;
|
||||
grid-template-rows: auto;
|
||||
const CardRowsContainer = styled.div`
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const CardActionContainer = styled.span`
|
||||
grid-column: -1;
|
||||
grid-row: 1;
|
||||
margin-top: -0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
`;
|
||||
|
||||
type Props = LiHTMLAttributes<HTMLLIElement> & {
|
||||
type Props = HTMLAttributes<HTMLElement> & {
|
||||
action?: React.ReactElement;
|
||||
/**
|
||||
* @default 'div'
|
||||
*/
|
||||
as?: keyof ReactHTML | ComponentType<HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> }>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
const Card = React.forwardRef<HTMLLIElement, Props>(({ className, children, action, ...props }, ref) => (
|
||||
<CardElement className={classNames(className, "is-relative", "p-2")} ref={ref} {...props}>
|
||||
{children}
|
||||
{action ? <CardActionContainer>{action}</CardActionContainer> : null}
|
||||
</CardElement>
|
||||
));
|
||||
const Card = React.forwardRef<HTMLElement, Props>(({ className, children, as: Comp = "div", action, ...props }, ref) =>
|
||||
React.createElement(
|
||||
Comp,
|
||||
{
|
||||
className: classNames(className, "is-relative", "is-flex", "scmm-card"),
|
||||
ref,
|
||||
...props,
|
||||
},
|
||||
<CardRowsContainer className="is-flex is-flex-direction-column is-justify-content-center is-flex-grow-1">
|
||||
{children}
|
||||
</CardRowsContainer>,
|
||||
action ? <span className="ml-2">{action}</span> : null
|
||||
)
|
||||
);
|
||||
|
||||
export default Card;
|
||||
@@ -22,24 +22,10 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { HTMLAttributes } from "react";
|
||||
import styled from "styled-components";
|
||||
import classNames from "classnames";
|
||||
|
||||
const CardRowElement = styled.div`
|
||||
grid-column: 1 / 2;
|
||||
`;
|
||||
|
||||
type Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
/**
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
const CardRow = React.forwardRef<HTMLDivElement, Props>(({ className, children, ...props }, ref) => (
|
||||
<CardRowElement className={classNames(className)} ref={ref} {...props}>
|
||||
{children}
|
||||
</CardRowElement>
|
||||
));
|
||||
const CardRow = "div";
|
||||
|
||||
export default CardRow;
|
||||
@@ -23,38 +23,14 @@
|
||||
*/
|
||||
|
||||
import React, { HTMLAttributes } from "react";
|
||||
import styled from "styled-components";
|
||||
import classNames from "classnames";
|
||||
|
||||
const CardTitleElement = styled.h3`
|
||||
a {
|
||||
color: var(--scm-secondary-text);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
&::after {
|
||||
outline: #af3ee7 3px solid;
|
||||
outline-offset: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
background-color: var(--scm-hover-color-blue);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = HTMLAttributes<HTMLHeadingElement>;
|
||||
type Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||
/**
|
||||
* @default 3
|
||||
*/
|
||||
level?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* A card title may contain a link as its only child which will be automatically stretched to cover the whole card area.
|
||||
@@ -70,10 +46,16 @@ type Props = HTMLAttributes<HTMLHeadingElement>;
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
const CardTitle = React.forwardRef<HTMLHeadingElement, Props>(({ children, className, ...props }, ref) => (
|
||||
<CardTitleElement className={classNames(className, "is-ellipsis-overflow")} ref={ref} {...props}>
|
||||
{children}
|
||||
</CardTitleElement>
|
||||
));
|
||||
const CardTitle = React.forwardRef<HTMLHeadingElement, Props>(({ children, level = 3, className, ...props }, ref) =>
|
||||
React.createElement(
|
||||
`h${level}`,
|
||||
{
|
||||
className: classNames(className, "is-ellipsis-overflow"),
|
||||
ref,
|
||||
...props,
|
||||
},
|
||||
children
|
||||
)
|
||||
);
|
||||
|
||||
export default CardTitle;
|
||||
@@ -22,13 +22,12 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import CardListComponent, { CardListBox as CardListBoxComponent } from "./card-list/CardList";
|
||||
import CardRow from "./card-list/CardRow";
|
||||
import Card from "./card-list/Card";
|
||||
import CardTitle from "./card-list/CardTitle";
|
||||
import CardListComponent, { CardListBox as CardListBoxComponent, CardListCard } from "./card-list/CardList";
|
||||
import CardTitle from "./card/CardTitle";
|
||||
import CardRow from "./card/CardRow";
|
||||
|
||||
const CardListExport = {
|
||||
Card: Object.assign(Card, {
|
||||
Card: Object.assign(CardListCard, {
|
||||
Row: CardRow,
|
||||
Title: CardTitle,
|
||||
}),
|
||||
|
||||
54
scm-ui/ui-styles/src/components/_card.scss
Normal file
54
scm-ui/ui-styles/src/components/_card.scss
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
.scmm-card {
|
||||
padding: 0.5rem;
|
||||
|
||||
*:is(h1, h2, h3, h4, h5, h6) a {
|
||||
color: var(--scm-secondary-text);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
&::after {
|
||||
outline: #af3ee7 2px solid;
|
||||
outline-offset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
background-color: var(--scm-hover-color-blue);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,3 +26,4 @@
|
||||
@import "bulma-popover/css/bulma-popover";
|
||||
@import "../components/_main.scss";
|
||||
@import "../components/_tooltip.scss";
|
||||
@import "../components/_card.scss";
|
||||
|
||||
Reference in New Issue
Block a user