diff --git a/web/app-list/app-list-header.css b/web/app-list/app-list-header.css --- a/web/app-list/app-list-header.css +++ b/web/app-list/app-list-header.css @@ -2,11 +2,7 @@ display: flex; justify-content: space-between; align-items: center; - background-color: var(--card-background-primary-default); padding: 16px; - box-shadow: 0px 1px 3px 0px var(--card-headerShadow-primary-default); - z-index: 0; - border-radius: 8px 8px 0 0; } .headerLabel { diff --git a/web/app-list/app-list.css b/web/app-list/app-list.css --- a/web/app-list/app-list.css +++ b/web/app-list/app-list.css @@ -1,6 +1,4 @@ .container { - display: flex; - flex-direction: column; min-width: 160px; } @@ -8,8 +6,5 @@ display: flex; flex-direction: column; row-gap: 16px; - background-color: var(--card-background-primary-default); padding: 16px; - flex: 1; - border-radius: 0 0 8px 8px; } diff --git a/web/app-list/app-list.react.js b/web/app-list/app-list.react.js --- a/web/app-list/app-list.react.js +++ b/web/app-list/app-list.react.js @@ -8,6 +8,7 @@ import AppListHeader from './app-list-header.react.js'; import AppListItem from './app-list-item.react.js'; import css from './app-list.css'; +import Card from '../components/card.react.js'; import { updateNavInfoActionType } from '../redux/action-types.js'; import { useSelector } from '../redux/redux-utils.js'; @@ -82,10 +83,13 @@ const appList = React.useMemo( () => ( -
- - {appListBody} -
+ + } + body={appListBody} + className={css.container} + /> + ), [appListBody], ); diff --git a/web/components/card.css b/web/components/card.css new file mode 100644 --- /dev/null +++ b/web/components/card.css @@ -0,0 +1,28 @@ +.container { + display: flex; +} + +.itemContainer { + display: flex; + flex-direction: column; + flex: 1; + background-color: var(--card-background-primary-default); + border-radius: 8px; +} + +.secondaryItemContainer { + background-color: var(--card-background-secondary-default); +} + +.firstItemContainer { + border-radius: 8px 0 0 8px; +} + +.lastItemContainer { + border-radius: 0 8px 8px 0; +} + +.headerShadow { + box-shadow: 0px 1px 3px 0px var(--card-headerShadow-primary-default); + clip-path: inset(0 0 -3px 0); +} diff --git a/web/components/card.react.js b/web/components/card.react.js new file mode 100644 --- /dev/null +++ b/web/components/card.react.js @@ -0,0 +1,64 @@ +// @flow + +import classnames from 'classnames'; +import * as React from 'react'; + +import css from './card.css'; + +type CardItemProps = { + +header: React.Node, + +body: React.Node, + +className?: string, +}; + +function CardItem(props: CardItemProps): React.Node { + const { header, body } = props; + + return ( + <> +
{header}
+ {body} + + ); +} + +type CardContainerProps = { + +children: React.ChildrenArray>, +}; + +function CardContainer(props: CardContainerProps): React.Node { + const { children } = props; + + const items = React.useMemo( + () => + React.Children.map(children, (child, index) => { + if (!child) { + return null; + } + + const numOfCards = React.Children.count(children); + + const className = classnames( + { + [css.itemContainer]: true, + [css.secondaryItemContainer]: index % 2 === 1, + [css.firstItemContainer]: numOfCards > 1 && index === 0, + [css.lastItemContainer]: numOfCards > 1 && index === numOfCards - 1, + }, + child.props.className, + ); + + return
{child}
; + }), + [children], + ); + + return
{items}
; +} + +const Card = { + Container: CardContainer, + Item: CardItem, +}; + +export default Card; diff --git a/web/theme.css b/web/theme.css --- a/web/theme.css +++ b/web/theme.css @@ -267,6 +267,7 @@ /* Card */ --card-background-primary-default: var(--shades-black-85); + --card-background-secondary-default: var(--shades-black-90); --card-headerShadow-primary-default: var(--shadow-dark-35); /* Button */ @@ -353,6 +354,7 @@ /* Card */ /* @GINSU: TODO double check these values after redesign is finished */ --card-background-primary-default: var(--shades-white-80); + --card-background-secondary-default: var(--shades-white-90); --card-headerShadow-primary-default: var(--shadow-light-35); /* Button */