diff --git a/apps/storybook/CHANGELOG.md b/apps/storybook/CHANGELOG.md index e159572c..454f4073 100644 --- a/apps/storybook/CHANGELOG.md +++ b/apps/storybook/CHANGELOG.md @@ -1,5 +1,12 @@ # @syntax/storybook +## 0.13.5 + +### Patch Changes + +- Updated dependencies [cbcfc85] + - @cambly/syntax-core@10.12.0 + ## 0.13.4 ### Patch Changes diff --git a/apps/storybook/package.json b/apps/storybook/package.json index d330cc68..444da9f7 100644 --- a/apps/storybook/package.json +++ b/apps/storybook/package.json @@ -1,6 +1,6 @@ { "name": "@syntax/storybook", - "version": "0.13.4", + "version": "0.13.5", "private": true, "scripts": { "dev": "NODE_OPTIONS=--openssl-legacy-provider storybook dev -p 6006", @@ -9,7 +9,7 @@ "clean": "rm -rf .turbo && rm -rf node_modules" }, "dependencies": { - "@cambly/syntax-core": "workspace:10.11.0", + "@cambly/syntax-core": "workspace:10.12.0", "@cambly/syntax-design-tokens": "workspace:0.11.1", "@cambly/syntax-floating-components": "workspace:^0.5.0", "react": "18.2.0", diff --git a/packages/syntax-core/CHANGELOG.md b/packages/syntax-core/CHANGELOG.md index dda83d6c..a0222b45 100644 --- a/packages/syntax-core/CHANGELOG.md +++ b/packages/syntax-core/CHANGELOG.md @@ -1,5 +1,11 @@ # @cambly/syntax-core +## 10.12.0 + +### Minor Changes + +- cbcfc85: Cambio: add Avatar & AvatarGroup updates + ## 10.11.0 ### Minor Changes diff --git a/packages/syntax-core/package.json b/packages/syntax-core/package.json index 0995db52..2ac1aa73 100644 --- a/packages/syntax-core/package.json +++ b/packages/syntax-core/package.json @@ -1,7 +1,7 @@ { "name": "@cambly/syntax-core", "description": "Cambly design system core components", - "version": "10.11.0", + "version": "10.12.0", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.js", @@ -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 ( - + ( - - - - {Icon && } - - {text} - - - - -); +}): JSX.Element => { + const { themeName } = useTheme(); + + return ( + + + + {Icon && } + + {text} + + + + + ); +}; export default Badge; diff --git a/packages/syntax-core/src/Box/Box.module.css b/packages/syntax-core/src/Box/Box.module.css index 861abf89..f6da4bfe 100644 --- a/packages/syntax-core/src/Box/Box.module.css +++ b/packages/syntax-core/src/Box/Box.module.css @@ -119,6 +119,10 @@ display: inline-block; } +.inlineFlex { + display: inline-flex; +} + .none { display: none; } @@ -149,6 +153,10 @@ display: inline-block; } + .inlineFlexSmall { + display: inline-flex; + } + .noneSmall { display: none; } @@ -180,6 +188,10 @@ display: inline-block; } + .inlineFlexLarge { + display: inline-flex; + } + .noneLarge { display: none; } diff --git a/packages/syntax-core/src/Box/Box.tsx b/packages/syntax-core/src/Box/Box.tsx index 61aa26e0..2a8570c5 100644 --- a/packages/syntax-core/src/Box/Box.tsx +++ b/packages/syntax-core/src/Box/Box.tsx @@ -7,6 +7,7 @@ import type allColors from "../colors/allColors"; import colorStyles from "../colors/colors.module.css"; import roundingStyles from "../rounding.module.css"; import { forwardRef } from "react"; +import { useTheme } from "../ThemeProvider/ThemeProvider"; type AlignItems = "baseline" | "center" | "end" | "start" | "stretch"; type As = @@ -24,7 +25,13 @@ type As = | "summary"; type Dimension = number | string; type Direction = "row" | "column"; -type Display = "none" | "flex" | "block" | "inlineBlock" | "visuallyHidden"; +type Display = + | "none" + | "flex" + | "block" + | "inlineBlock" + | "inlineFlex" + | "visuallyHidden"; type Gap = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; type JustifyContent = | "start" @@ -353,6 +360,7 @@ type BoxProps = { /** * Border radius of the box. * + * Classic: * * `none`: 0px * * `sm`: 8px * * `md`: 12px @@ -360,6 +368,14 @@ type BoxProps = { * * `xl`: 24px * * `full`: 999px * + * Cambio: + * * `none`: 0px + * * `sm`: 4px + * * `md`: 8px + * * `lg`: 8px (maps to `md`) + * * `xl`: 8px (maps to `md`) + * * `full`: 999px + * * @defaultValue "none" */ rounding?: "xl" | "lg" | "md" | "sm" | "full" | "none"; @@ -417,6 +433,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. * @@ -429,6 +454,7 @@ const Box = forwardRef(function Box( ref, ): ReactElement { const { as: BoxElement = "div", children, ...boxProps } = props; + const { themeName } = useTheme(); const { // Classname @@ -492,6 +518,15 @@ const Box = forwardRef(function Box( ...maybePassThroughProps } = boxProps; + const classicRoundingStyle = + themeName === "classic" && rounding && rounding !== "none" + ? roundingStyles[`rounding${rounding}`] + : undefined; + const cambioRoundingStyles = + themeName === "cambio" && rounding && rounding !== "none" + ? roundingStyles[`rounding${roundingCambio(rounding)}Cambio`] + : undefined; + const parsedProps = { className: classNames( styles.box, @@ -575,7 +610,8 @@ 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}`], + 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 ( { + 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 (