From 85d3e525feebccbc72fa31f48f5420e8d1c76357 Mon Sep 17 00:00:00 2001 From: Gregor Adams Date: Thu, 23 Sep 2021 18:38:47 +0200 Subject: [PATCH] feat: add masonry grid --- src/atoms/box/index.tsx | 44 +++++++++++ src/molecules/masonry/column.tsx | 13 ++++ src/molecules/masonry/grid.tsx | 33 ++++++++ src/molecules/masonry/styled.ts | 34 ++++++++ src/pages/design-system/masonry/index.tsx | 22 ++++++ src/templates/design-system/index.tsx | 3 + src/templates/design-system/pages/masonry.tsx | 78 +++++++++++++++++++ 7 files changed, 227 insertions(+) create mode 100644 src/atoms/box/index.tsx create mode 100644 src/molecules/masonry/column.tsx create mode 100644 src/molecules/masonry/grid.tsx create mode 100644 src/molecules/masonry/styled.ts create mode 100644 src/pages/design-system/masonry/index.tsx create mode 100644 src/templates/design-system/pages/masonry.tsx diff --git a/src/atoms/box/index.tsx b/src/atoms/box/index.tsx new file mode 100644 index 0000000..4c11b9a --- /dev/null +++ b/src/atoms/box/index.tsx @@ -0,0 +1,44 @@ +import { css } from "@emotion/react"; +import styled from "@emotion/styled"; +import React, { CSSProperties, FC } from "react"; + +const StyledBox = styled.div<{ + style?: CSSProperties & { "--aspect-ratio"?: number }; + roundCorners?: boolean; +}>` + position: relative; + width: 100%; + height: 0; + padding-bottom: calc(100% / var(--aspect-ratio, 1)); + ${({ theme, roundCorners }) => + roundCorners && + css` + border-radius: ${theme.shapes.m}; + overflow: hidden; + `}; +`; + +const StyledBoxInner = styled.div` + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; +`; + +export interface BoxProps { + aspectRatio?: number; + roundCorners?: boolean; +} + +const Box: FC = ({ aspectRatio = 1, roundCorners, children, ...props }) => { + return ( + + {children} + + ); +}; + +export default Box; diff --git a/src/molecules/masonry/column.tsx b/src/molecules/masonry/column.tsx new file mode 100644 index 0000000..37ad83f --- /dev/null +++ b/src/molecules/masonry/column.tsx @@ -0,0 +1,13 @@ +import { BoxProps } from "@/atoms/box"; +import { StyledMasonryBox } from "@/molecules/masonry/styled"; +import React, { FC } from "react"; + +const MasonryColumn: FC = ({ aspectRatio, roundCorners, children, ...props }) => { + return ( + + {children} + + ); +}; + +export default MasonryColumn; diff --git a/src/molecules/masonry/grid.tsx b/src/molecules/masonry/grid.tsx new file mode 100644 index 0000000..0eac0f0 --- /dev/null +++ b/src/molecules/masonry/grid.tsx @@ -0,0 +1,33 @@ +import { StyledMasonryGrid } from "@/molecules/masonry/styled"; +import React, { FC } from "react"; + +export interface MasonryGridProps { + colCountXS?: number; + colCountS?: number; + colCountM?: number; + colCountL?: number; +} +const MasonryGrid: FC = ({ + colCountXS = "var(--col-span)", + colCountS = colCountXS, + colCountM = colCountS, + colCountL = colCountM, + children, + ...props +}) => { + return ( + + {children} + + ); +}; + +export default MasonryGrid; diff --git a/src/molecules/masonry/styled.ts b/src/molecules/masonry/styled.ts new file mode 100644 index 0000000..2addcbf --- /dev/null +++ b/src/molecules/masonry/styled.ts @@ -0,0 +1,34 @@ +import Box from "@/atoms/box"; +import { css } from "@emotion/react"; +import styled from "@emotion/styled"; +import { CSSProperties } from "react"; + +export const StyledMasonryGrid = styled.div<{ + style?: CSSProperties & { + "--col-count-xs"?: number | string; + "--col-count-s"?: number | string; + "--col-count-m"?: number | string; + "--col-count-l"?: number | string; + }; +}>` + --col-count: var(--col-count-xs); + + column-gap: var(--gap-x); + column-count: var(--col-count); + ${({ theme }) => css` + ${theme.mq.s} { + --col-count: var(--col-count-s); + } + ${theme.mq.m} { + --col-count: var(--col-count-m); + } + ${theme.mq.l} { + --col-count: var(--col-count-l); + } + `}; +`; + +export const StyledMasonryBox = styled(Box)` + margin-bottom: var(--gap-x); + break-inside: avoid; +`; diff --git a/src/pages/design-system/masonry/index.tsx b/src/pages/design-system/masonry/index.tsx new file mode 100644 index 0000000..842aa16 --- /dev/null +++ b/src/pages/design-system/masonry/index.tsx @@ -0,0 +1,22 @@ +import { addApolloState, initializeApollo } from "@/ions/services/apollo/client"; +import Examples from "@/templates/design-system/pages/masonry"; +import { PageProps, StaticPageProps } from "@/types"; +import { GetStaticProps, NextPage } from "next"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; +import React from "react"; + +const Page: NextPage = () => { + return ; +}; + +export const getStaticProps: GetStaticProps = async context => { + const apolloClient = initializeApollo(); + return addApolloState(apolloClient, { + props: { + ...(await serverSideTranslations(context.locale)), + locale: context.locale, + }, + }); +}; + +export default Page; diff --git a/src/templates/design-system/index.tsx b/src/templates/design-system/index.tsx index 2448db2..a519827 100644 --- a/src/templates/design-system/index.tsx +++ b/src/templates/design-system/index.tsx @@ -42,6 +42,9 @@ const DesignSystem = () => {
  • Grid
  • +
  • + Masonry +
  • Snackbar
  • diff --git a/src/templates/design-system/pages/masonry.tsx b/src/templates/design-system/pages/masonry.tsx new file mode 100644 index 0000000..b9798dc --- /dev/null +++ b/src/templates/design-system/pages/masonry.tsx @@ -0,0 +1,78 @@ +import Typography from "@/atoms/typography"; +import Layout from "@/groups/layout"; +import { RawBreadcrumb } from "@/ions/contexts/breadcrumbs/types"; +import { Column, Grid } from "@/molecules/grid"; +import MasonryColumn from "@/molecules/masonry/column"; +import MasonryGrid from "@/molecules/masonry/grid"; +import Breadcrumbs from "@/organisms/breadcrumbs"; +import OverlayGrid from "@/organisms/grid-overlay"; +import { css } from "@emotion/react"; +import styled from "@emotion/styled"; +import { useTranslation } from "next-i18next"; +import process from "process"; +import React, { useMemo } from "react"; + +const aspectRatios = [1, 16 / 9, 3 / 4, 9 / 16, 4 / 3]; +const demoItems = Array.from({ length: 15 }).map((item, index) => ({ + id: index + 1, + color: `hsl(${index * 129}, 60%, 80%)`, + aspectRatio: + aspectRatios[ + Math.round(index + (index + 1.1424) * 1.1315 + (index + 1.1358) * 1.1372) % + aspectRatios.length + ], +})); + +const StyledColoredBox = styled.div` + width: 100%; + height: 100%; + ${({ theme }) => css` + border-radius: ${theme.shapes.s}; + box-shadow: ${theme.shadows.s}; + `} +`; + +const MasonryExamples = () => { + const { t } = useTranslation(["navigation"]); + const breadcrumbs: RawBreadcrumb[] = useMemo( + () => [ + { + href: "/", + title: t("navigation:home"), + }, + { + href: "/design-system", + title: "Design System", + }, + { + href: "/design-system/masonry", + title: "Masonry", + }, + ], + [t] + ); + return ( + + + + + Masonry + + + + {demoItems.map(item => { + return ( + + + + ); + })} + + + + {process.env.NODE_ENV === "production" && } + + ); +}; + +export default MasonryExamples;