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(
() => (
-
+
+ }
+ 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 */