From 41bd587e74b76616efb0cabbcd101919fb78f848 Mon Sep 17 00:00:00 2001 From: Christian Vuerings Date: Wed, 6 Mar 2024 11:18:28 -0800 Subject: [PATCH 1/6] Cambio: add Avatar & AvatarGroup updates --- .changeset/strange-months-thank.md | 5 +++ packages/syntax-core/package.json | 2 +- .../syntax-core/src/Avatar/Avatar.module.css | 27 ++++++++++++ packages/syntax-core/src/Avatar/Avatar.tsx | 42 +++++++++++++++++-- .../src/AvatarGroup/AvatarGroup.tsx | 20 ++++++++- packages/syntax-core/src/Box/Box.tsx | 20 ++++++++- .../src/ThemeProvider/ThemeProvider.tsx | 8 ++-- 7 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 .changeset/strange-months-thank.md diff --git a/.changeset/strange-months-thank.md b/.changeset/strange-months-thank.md new file mode 100644 index 00000000..5054d96b --- /dev/null +++ b/.changeset/strange-months-thank.md @@ -0,0 +1,5 @@ +--- +"@cambly/syntax-core": minor +--- + +Cambio: add Avatar & AvatarGroup updates diff --git a/packages/syntax-core/package.json b/packages/syntax-core/package.json index 0995db52..74e5b4b0 100644 --- a/packages/syntax-core/package.json +++ b/packages/syntax-core/package.json @@ -14,7 +14,7 @@ "scripts": { "build": "tsup", "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", - "dev": "tsup --watch", + "dev": "NODE_OPTIONS=--max_old_space_size=4096 tsup --watch", "lint": "TIMING=1 eslint \"src/**/*.ts*\" --max-warnings 0", "stylelint": "stylelint \"**/*.css\"", "stylelint:fix": "npm run stylelint --fix", diff --git a/packages/syntax-core/src/Avatar/Avatar.module.css b/packages/syntax-core/src/Avatar/Avatar.module.css index b4410398..0f46ad2b 100644 --- a/packages/syntax-core/src/Avatar/Avatar.module.css +++ b/packages/syntax-core/src/Avatar/Avatar.module.css @@ -8,9 +8,16 @@ background-size: cover; background-repeat: no-repeat; border-radius: 50%; +} + +.avatarImageClassic { border: 2px solid #fff; } +.avatarImageOutlineCambio { + border: 2px solid var(--color-cambio-gray-100); +} + .sm { width: 24px; height: 24px; @@ -30,3 +37,23 @@ width: 128px; height: 129px; } + +.smCambio { + width: 32px; + height: 32px; +} + +.mdCambio { + width: 48px; + height: 48px; +} + +.lgCambio { + width: 64px; + height: 64px; +} + +.xlCambio { + width: 64px; + height: 64px; +} diff --git a/packages/syntax-core/src/Avatar/Avatar.tsx b/packages/syntax-core/src/Avatar/Avatar.tsx index 92aff5b3..63fc36af 100644 --- a/packages/syntax-core/src/Avatar/Avatar.tsx +++ b/packages/syntax-core/src/Avatar/Avatar.tsx @@ -3,6 +3,7 @@ import classNames from "classnames"; import styles from "./Avatar.module.css"; import Box from "../Box/Box"; import { useAvatarGroup } from "../AvatarGroup/AvatarGroup"; +import { useTheme } from "../ThemeProvider/ThemeProvider"; const sizeToIconStyles = { sm: { bottom: 6, marginInlineEnd: 2, height: 4, width: 4 }, @@ -11,30 +12,51 @@ const sizeToIconStyles = { xl: { bottom: 12, marginInlineEnd: 12, height: 16, width: 16 }, } as const; -const sizeToMargin = { +const sizeToMarginClassic = { sm: -16, md: -28, lg: -48, xl: -88, } as const; +const sizeToMarginCambio = { + sm: -12, + md: -20, + lg: -28, + xl: -28, +} as const; + function AvatarInternal({ accessibilityLabel, icon, + outline, size = "md", src, }: { accessibilityLabel: string; icon?: React.ReactElement; + outline?: boolean; size?: "sm" | "md" | "lg" | "xl"; src: string; }): ReactElement { + const { themeName } = useTheme(); + return ( -
+
{accessibilityLabel} {icon && ( @@ -81,11 +103,18 @@ const Avatar = ({ /** * Size of the avatar. * + * Classic: * * `sm`: 24px * * `md`: 40px * * `lg`: 72px * * `xl`: 128px * + * Cambio: + * * `sm`: 32px + * * `md`: 48px + * * `lg`: 64px + * * `xl`: 64px (deprecated, maps to `lg` in Cambio) + * * @defaultValue `md` */ size?: "sm" | "md" | "lg" | "xl"; @@ -95,6 +124,7 @@ const Avatar = ({ src: string; }): JSX.Element => { const avatarGroupContext = useAvatarGroup(); + const { themeName } = useTheme(); if (avatarGroupContext !== null) { return ( @@ -102,7 +132,10 @@ const Avatar = ({ position="relative" dangerouslySetInlineStyle={{ __style: { - marginInlineEnd: sizeToMargin[avatarGroupContext.size], + marginInlineEnd: + themeName === "cambio" + ? sizeToMarginCambio[avatarGroupContext.size] + : sizeToMarginClassic[avatarGroupContext.size], }, }} > @@ -116,6 +149,7 @@ const Avatar = ({ diff --git a/packages/syntax-core/src/AvatarGroup/AvatarGroup.tsx b/packages/syntax-core/src/AvatarGroup/AvatarGroup.tsx index 59718b88..1f56955e 100644 --- a/packages/syntax-core/src/AvatarGroup/AvatarGroup.tsx +++ b/packages/syntax-core/src/AvatarGroup/AvatarGroup.tsx @@ -5,8 +5,14 @@ import { type ReactElement, } from "react"; import Box from "../Box/Box"; +import { useTheme } from "../ThemeProvider/ThemeProvider"; -type Size = "sm" | "md" | "lg" | "xl"; +type Size = + | "sm" + | "md" + | "lg" + /* `xl` is deprecated and mapped to `lg` in Cambio */ + | "xl"; type Orientation = "standard" | "reverse"; type AvatarGroupContextType = { @@ -43,11 +49,18 @@ export default function AvatarGroup({ /** * Size of the avatars in the AvatarGroup. * + * Classic: * * `sm`: 24px * * `md`: 40px * * `lg`: 72px * * `xl`: 128px * + * Cambio: + * * `sm`: 32px + * * `md`: 48px + * * `lg`: 64px + * * `xl`: 64px (deprecated, maps to `lg` in Cambio) + * * @defaultValue `md` */ size?: Size; @@ -65,8 +78,11 @@ export default function AvatarGroup({ */ children: ReactNode; }): ReactElement { + const { themeName } = useTheme(); + const parsedSize = themeName === "cambio" && size === "xl" ? "lg" : size; + return ( - + (function Box( ref, ): ReactElement { const { as: BoxElement = "div", children, ...boxProps } = props; + const { themeName } = useTheme(); const { // Classname @@ -492,6 +503,11 @@ const Box = forwardRef(function Box( ...maybePassThroughProps } = boxProps; + const parsedRounding = + themeName === "cambio" && rounding && ["md", "lg", "xl"].includes(rounding) + ? "sm" + : rounding; + const parsedProps = { className: classNames( styles.box, @@ -575,7 +591,9 @@ const Box = forwardRef(function Box( smJustifyContent && styles[`justifyContent${smJustifyContent}Small`], lgJustifyContent && styles[`justifyContent${lgJustifyContent}Large`], position && position !== "static" && styles[position], - rounding && rounding !== "none" && roundingStyles[`rounding${rounding}`], + parsedRounding && + parsedRounding !== "none" && + roundingStyles[`rounding${parsedRounding}`], overflow && styles[`overflow${overflow}`], overflowX && styles[`overflowX${overflowX}`], overflowY && styles[`overflowY${overflowY}`], diff --git a/packages/syntax-core/src/ThemeProvider/ThemeProvider.tsx b/packages/syntax-core/src/ThemeProvider/ThemeProvider.tsx index 601dcf98..1fb8f42e 100644 --- a/packages/syntax-core/src/ThemeProvider/ThemeProvider.tsx +++ b/packages/syntax-core/src/ThemeProvider/ThemeProvider.tsx @@ -15,10 +15,10 @@ ThemeContext.displayName = "ThemeContext"; const classicToCambioKeyLookup = { "color-base-black": "color-cambio-black", "color-base-destructive-100": "color-cambio-destructive-100", - "color-base-destructive-200": undefined, // Deprecated - to be deleted + "color-base-destructive-200": "color-cambio-destructive-300", "color-base-destructive-300": "color-cambio-destructive-300", "color-base-destructive-700": "color-cambio-destructive-700", - "color-base-destructive-800": undefined, // Deprecated - to be deleted + "color-base-destructive-800": "color-cambio-destructive-900", "color-base-destructive-900": "color-cambio-destructive-900", "color-base-gray-10": "color-cambio-gray-370", "color-base-gray-30": "color-cambio-gray-370", @@ -43,10 +43,10 @@ const classicToCambioKeyLookup = { "color-base-primary-800": "color-cambio-gray-800", "color-base-primary-900": "color-cambio-gray-900", "color-base-success-100": "color-cambio-success-100", - "color-base-success-200": undefined, // Deprecated - to be deleted + "color-base-success-200": "color-cambio-success-300", "color-base-success-300": "color-cambio-success-300", "color-base-success-700": "color-cambio-success-700", - "color-base-success-800": undefined, // Deprecated - to be deleted + "color-base-success-800": "color-cambio-success-900", "color-base-success-900": "color-cambio-success-900", "color-base-purple-100": undefined, // Deprecated - to be deleted "color-base-purple-200": undefined, // Deprecated - to be deleted From be6108ea427b567a38330f229cb54b99af9e9fe6 Mon Sep 17 00:00:00 2001 From: Christian Vuerings Date: Fri, 8 Mar 2024 07:19:35 -0800 Subject: [PATCH 2/6] TEST --- packages/syntax-core/src/Box/Box.tsx | 34 +++++++++++++------ .../src/ButtonGroup/ButtonGroup.module.css | 12 +++++++ .../src/ButtonGroup/ButtonGroup.tsx | 28 ++++++++++++--- packages/syntax-core/src/Card/Card.tsx | 22 +++++++++--- packages/syntax-core/src/rounding.module.css | 13 +++++++ 5 files changed, 89 insertions(+), 20 deletions(-) diff --git a/packages/syntax-core/src/Box/Box.tsx b/packages/syntax-core/src/Box/Box.tsx index 214c1b18..7268e46f 100644 --- a/packages/syntax-core/src/Box/Box.tsx +++ b/packages/syntax-core/src/Box/Box.tsx @@ -364,10 +364,10 @@ type BoxProps = { * * Cambio: * * `none`: 0px - * * `sm`: 8px - * * `md`: 8px (maps to `sm`) - * * `lg`: 8px (maps to `sm`) - * * `xl`: 8px (maps to `sm`) + * * `sm`: 4px + * * `md`: 8px + * * `lg`: 8px (maps to `md`) + * * `xl`: 8px (maps to `md`) * * `full`: 999px * * @defaultValue "none" @@ -427,6 +427,15 @@ type BoxProps = { width?: Dimension; }; +function roundingCambio( + rounding: "sm" | "md" | "lg" | "xl" | "full", +): "sm" | "md" | "full" { + if (rounding === "lg" || rounding === "xl") { + return "md"; + } + return rounding; +} + /** * [Box](https://cambly-syntax.vercel.app/?path=/docs/components-box--docs) is primitive design component and is used by lots of other components. It keeps details like spacing, borders and colors consistent across all of Syntax. * @@ -503,10 +512,14 @@ const Box = forwardRef(function Box( ...maybePassThroughProps } = boxProps; - const parsedRounding = - themeName === "cambio" && rounding && ["md", "lg", "xl"].includes(rounding) - ? "sm" - : rounding; + const classicRoundingStyle = + themeName === "classic" && rounding && rounding !== "none" + ? roundingStyles[`rounding${rounding}`] + : undefined; + const cambioRoundingStyles = + themeName === "cambio" && rounding && rounding !== "none" + ? roundingStyles[`rounding${roundingCambio(rounding)}`] + : undefined; const parsedProps = { className: classNames( @@ -591,9 +604,8 @@ const Box = forwardRef(function Box( smJustifyContent && styles[`justifyContent${smJustifyContent}Small`], lgJustifyContent && styles[`justifyContent${lgJustifyContent}Large`], position && position !== "static" && styles[position], - parsedRounding && - parsedRounding !== "none" && - roundingStyles[`rounding${parsedRounding}`], + classicRoundingStyle, + cambioRoundingStyles, overflow && styles[`overflow${overflow}`], overflowX && styles[`overflowX${overflowX}`], overflowY && styles[`overflowY${overflowY}`], diff --git a/packages/syntax-core/src/ButtonGroup/ButtonGroup.module.css b/packages/syntax-core/src/ButtonGroup/ButtonGroup.module.css index 69040199..641e6536 100644 --- a/packages/syntax-core/src/ButtonGroup/ButtonGroup.module.css +++ b/packages/syntax-core/src/ButtonGroup/ButtonGroup.module.css @@ -21,3 +21,15 @@ .largeGap { gap: 16px; } + +.smallGapCambio { + gap: 4px; +} + +.mediumGapCambio { + gap: 8px; +} + +.largeGapCambio { + gap: 12px; +} diff --git a/packages/syntax-core/src/ButtonGroup/ButtonGroup.tsx b/packages/syntax-core/src/ButtonGroup/ButtonGroup.tsx index f0792bbb..e8d9ac04 100644 --- a/packages/syntax-core/src/ButtonGroup/ButtonGroup.tsx +++ b/packages/syntax-core/src/ButtonGroup/ButtonGroup.tsx @@ -2,13 +2,20 @@ import { type ReactElement, type ReactNode } from "react"; import styles from "./ButtonGroup.module.css"; import { type Size } from "../constants"; import classNames from "classnames"; +import { useTheme } from "../ThemeProvider/ThemeProvider"; -const gap = { +const gapClassic = { sm: styles.smallGap, md: styles.mediumGap, lg: styles.largeGap, } as const; +const gapCambio = { + sm: styles.smallGapCambio, + md: styles.mediumGapCambio, + lg: styles.largeGapCambio, +} as const; + /** * [ButtonGroup](https://cambly-syntax.vercel.app/?path=/docs/components-buttongroup--docs) groups buttons in a row or column with consistent spacing between each button. */ @@ -26,10 +33,16 @@ const ButtonGroup = ({ /** * The size of the button group defines the spacing between each button * + * Classic: * * `sm`: 8px * * `md`: 12px * * `lg`: 16px * + * Cambio: + * * `sm`: 4px + * * `md`: 8px + * * `lg`: 12px + * * @defaultValue "md" */ size?: (typeof Size)[number]; @@ -38,10 +51,15 @@ const ButtonGroup = ({ */ children?: ReactNode; }): ReactElement => { - const classnames = classNames(styles.buttonGroup, gap[size], { - [styles.horizontal]: orientation === "horizontal", - [styles.vertical]: orientation === "vertical", - }); + const { themeName } = useTheme(); + const classnames = classNames( + styles.buttonGroup, + themeName === "classic" ? gapClassic[size] : gapCambio[size], + { + [styles.horizontal]: orientation === "horizontal", + [styles.vertical]: orientation === "vertical", + }, + ); return
{children}
; }; diff --git a/packages/syntax-core/src/Card/Card.tsx b/packages/syntax-core/src/Card/Card.tsx index e0ed1dee..1c153b2c 100644 --- a/packages/syntax-core/src/Card/Card.tsx +++ b/packages/syntax-core/src/Card/Card.tsx @@ -1,4 +1,5 @@ import Box from "../Box/Box"; +import { useTheme } from "../ThemeProvider/ThemeProvider"; import type allColors from "../colors/allColors"; type CardType = { @@ -15,6 +16,15 @@ type CardType = { * The child components to render within Card. */ children: JSX.Element; + /** + * The size of the card (Cambio only) which specifies the padding and spacing of the card. + * + * `compact`: 8px padding + * `roomy`: 16px padding + * + * @defaultValue `roomy` + */ + size?: "compact" | "roomy"; }; /** @@ -23,14 +33,18 @@ type CardType = { export default function Card({ backgroundColor = "white", children, + size, "data-testid": dataTestId, }: CardType): JSX.Element { + const { themeName } = useTheme(); + + const cambioPadding = size === "compact" ? 2 : 4; + return ( Date: Tue, 12 Mar 2024 07:27:51 -0700 Subject: [PATCH 3/6] TextArea etc updates --- .../src/Checkbox/Checkbox.module.css | 12 +++- .../syntax-core/src/Checkbox/Checkbox.tsx | 43 +++++++++--- .../src/Divider/Divider.module.css | 5 +- packages/syntax-core/src/Divider/Divider.tsx | 14 +++- .../src/RadioButton/RadioButton.tsx | 42 +++++++++--- .../src/TextArea/TextArea.module.css | 12 ++++ .../src/TextArea/TextArea.stories.tsx | 4 ++ .../syntax-core/src/TextArea/TextArea.tsx | 36 ++++++++-- .../src/TextField/TextField.module.css | 66 ++++++++++++++++++- .../syntax-core/src/TextField/TextField.tsx | 36 ++++++++-- 10 files changed, 236 insertions(+), 34 deletions(-) diff --git a/packages/syntax-core/src/Checkbox/Checkbox.module.css b/packages/syntax-core/src/Checkbox/Checkbox.module.css index fe71c4e6..c0dfaf3f 100644 --- a/packages/syntax-core/src/Checkbox/Checkbox.module.css +++ b/packages/syntax-core/src/Checkbox/Checkbox.module.css @@ -22,6 +22,10 @@ box-sizing: border-box; } +.cambioCheckbox { + border-radius: 4px; +} + .disabled { opacity: 0.5; } @@ -61,11 +65,17 @@ .sm { height: 16px; width: 16px; - border-radius: 6px; } .md { height: 24px; width: 24px; +} + +.smBorderRadius { + border-radius: 6px; +} + +.mdBorderRadius { border-radius: 8px; } diff --git a/packages/syntax-core/src/Checkbox/Checkbox.tsx b/packages/syntax-core/src/Checkbox/Checkbox.tsx index ca376fa0..33d047c7 100644 --- a/packages/syntax-core/src/Checkbox/Checkbox.tsx +++ b/packages/syntax-core/src/Checkbox/Checkbox.tsx @@ -5,6 +5,8 @@ import styles from "./Checkbox.module.css"; import focusStyles from "../Focus.module.css"; import Typography from "../Typography/Typography"; import useIsHydrated from "../useIsHydrated"; +import { useTheme } from "../ThemeProvider/ThemeProvider"; +import colorStyles from "../colors/colors.module.css"; const typographySize = { sm: 100, @@ -68,23 +70,44 @@ const Checkbox = ({ */ error?: boolean; }): ReactElement => { + const { themeName } = useTheme(); const isHydrated = useIsHydrated(); const disabled = !isHydrated || disabledProp; const [isFocused, setIsFocused] = useState(false); const { isFocusVisible } = useFocusVisible(); - const checkboxStyling = classNames(styles.checkbox, styles[size]); - const uncheckedStyling = classNames(checkboxStyling, styles.uncheckedBox, { - [styles.uncheckedBorder]: !error, - [styles.uncheckedErrorBorder]: error, - [focusStyles.accessibilityOutlineFocus]: isFocused && isFocusVisible, - }); - const checkedStyling = classNames(checkboxStyling, styles.checkedBox, { - [styles.checkedNonError]: !error, - [styles.checkedError]: error, + const checkboxStyling = classNames(styles.checkbox, styles[size], { [focusStyles.accessibilityOutlineFocus]: isFocused && isFocusVisible, }); + const classicCheckboxStyling = classNames( + checkboxStyling, + styles[`${size}BorderRadius`], + ); + + const cambioCheckboxStyling = classNames( + checkboxStyling, + styles.cambioCheckbox, + error + ? colorStyles.cambioDestructive370BackgroundColor + : colorStyles.cambioGray370BackgroundColor, + ); + + const uncheckedStyling = + themeName === "classic" + ? classNames(classicCheckboxStyling, styles.uncheckedBox, { + [styles.uncheckedBorder]: !error, + [styles.uncheckedErrorBorder]: error, + }) + : cambioCheckboxStyling; + const checkedStyling = + themeName === "classic" + ? classNames(classicCheckboxStyling, styles.checkedBox, { + [styles.checkedNonError]: !error, + [styles.checkedError]: error, + }) + : cambioCheckboxStyling; + return (