From 220437c51d5c8f73ad5343b5b0e4d77434d17565 Mon Sep 17 00:00:00 2001 From: Sam Holleworth Date: Thu, 30 Jan 2025 17:26:30 +0000 Subject: [PATCH] feat: give typography spacing props, add stack component --- .../client/src/client/components/Spacing.tsx | 97 +++++++++++++++++++ .../client/src/client/components/Stack.tsx | 40 ++++++++ .../src/client/components/Typography.tsx | 13 +-- 3 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 packages/client/src/client/components/Spacing.tsx create mode 100644 packages/client/src/client/components/Stack.tsx diff --git a/packages/client/src/client/components/Spacing.tsx b/packages/client/src/client/components/Spacing.tsx new file mode 100644 index 000000000..e78053497 --- /dev/null +++ b/packages/client/src/client/components/Spacing.tsx @@ -0,0 +1,97 @@ +import styled, { css } from "styled-components" + +import { Spacing as ThemeSpacing } from "../theme/types" + +// add custom css properties here +const customMarginPropNames = ["marginX", "marginY"] as const +const customPaddingPropNames = ["paddingX", "paddingY"] as const + +// add existing css properties here +const marginPropNames = [ + "margin", + "marginTop", + "marginBottom", + "marginLeft", + "marginRight", + ...customMarginPropNames, +] as const + +const paddingPropNames = [ + "padding", + "paddingTop", + "paddingBottom", + "paddingLeft", + "paddingRight", + ...customPaddingPropNames, +] as const + +type MarginKeys = (typeof marginPropNames)[number] +type PaddingKeys = (typeof paddingPropNames)[number] + +type MarginProps = { [k in MarginKeys]?: ThemeSpacing | number } + +type PaddingProps = { [k in PaddingKeys]?: ThemeSpacing | number } +export type SpacingProps = MarginProps & PaddingProps + +export const SpacingContainer = styled.div` + ${({ theme, ...props }) => { + const mapSpacingCss = (prop?: ThemeSpacing | number) => + prop && (typeof prop === "number" ? prop : theme.newTheme.spacing[prop]) + + const marginProps = Object.entries(props).filter( + ([name]) => + marginPropNames.includes(name as (typeof marginPropNames)[number]) && + !customMarginPropNames.includes( + name as (typeof customMarginPropNames)[number], + ), + ) + + const paddingProps = Object.entries(props).filter( + ([name]) => + paddingPropNames.includes(name as (typeof paddingPropNames)[number]) && + !customPaddingPropNames.includes( + name as (typeof customPaddingPropNames)[number], + ), + ) + + const { marginX, marginY, paddingX, paddingY } = props + + const customSpacingStyles = { + ...(paddingX && + Object.fromEntries([ + ["paddingLeft", mapSpacingCss(paddingX)], + ["paddingRight", mapSpacingCss(paddingX)], + ])), + ...(paddingY && + Object.fromEntries([ + ["paddingTop", mapSpacingCss(paddingY)], + ["paddingBottom", mapSpacingCss(paddingY)], + ])), + ...(marginX && + Object.fromEntries([ + ["marginLeft", mapSpacingCss(marginX)], + ["marginRight", mapSpacingCss(marginX)], + ])), + ...(marginY && + Object.fromEntries([ + ["marginTop", mapSpacingCss(marginY)], + ["marginBottom", mapSpacingCss(marginY)], + ])), + } + + const marginStyles = Object.fromEntries( + marginProps.map(([name, value]) => [name, mapSpacingCss(value)]), + ) + + const paddingStyles = Object.fromEntries( + paddingProps.map(([name, value]) => [name, mapSpacingCss(value)]), + ) + + return css({ + ...customSpacingStyles, + // the more specific styles should overwrite the more general custom styles + ...marginStyles, + ...paddingStyles, + }) + }} +` diff --git a/packages/client/src/client/components/Stack.tsx b/packages/client/src/client/components/Stack.tsx new file mode 100644 index 000000000..25410e9a2 --- /dev/null +++ b/packages/client/src/client/components/Stack.tsx @@ -0,0 +1,40 @@ +import styled, { css, CSSProperties } from "styled-components" + +import { Spacing } from "../theme/types" +import { SpacingContainer, SpacingProps } from "./Spacing" + +interface StackProps extends SpacingProps { + direction?: CSSProperties["flexDirection"] + wrap?: CSSProperties["flexWrap"] + flow?: CSSProperties["flexFlow"] + justifyContent?: CSSProperties["justifyContent"] + alignItems?: CSSProperties["alignItems"] + alignContent?: CSSProperties["alignContent"] + gap?: Spacing | number +} + +export const Stack = styled(SpacingContainer)` + display: flex; + + ${({ + theme, + direction, + wrap, + flow, + justifyContent, + alignItems, + alignContent, + gap, + }) => + css({ + flexDirection: direction, + flexWrap: wrap, + flexFlow: flow, + justifyContent, + alignItems, + alignContent, + ...(gap && { + gap: typeof gap === "number" ? gap : theme.newTheme.spacing[gap], + }), + })} +` diff --git a/packages/client/src/client/components/Typography.tsx b/packages/client/src/client/components/Typography.tsx index 35ee5f75f..675804cfb 100644 --- a/packages/client/src/client/components/Typography.tsx +++ b/packages/client/src/client/components/Typography.tsx @@ -1,20 +1,21 @@ -import styled from "styled-components" +import styled, { css, CSSProperties } from "styled-components" import { Color, TextStyles } from "../theme/types" +import { SpacingContainer, SpacingProps } from "./Spacing" -interface TypographyProps { +interface TypographyProps extends SpacingProps { variant?: TextStyles color?: Color allowLineHeight?: boolean + textTransform?: CSSProperties["textTransform"] } -export const Typography = styled.div` +export const Typography = styled(SpacingContainer)` ${({ variant, theme }) => variant ? theme.newTheme.textStyles[variant] : null} color: ${({ color, theme }) => color ? theme.newTheme.color[color] : "inherit"}; - - margin-block-end: 0; ${({ allowLineHeight }) => - allowLineHeight ? undefined : `line-height: normal`}; + allowLineHeight ? undefined : `line-height: normal;`} + ${({ textTransform }) => css({ textTransform })} `