diff --git a/packages/ui/src/Tag/Tag.stories.tsx b/packages/ui/src/Tag/Tag.stories.tsx new file mode 100644 index 00000000..4272ca21 --- /dev/null +++ b/packages/ui/src/Tag/Tag.stories.tsx @@ -0,0 +1,95 @@ +import { Box } from "@mui/material"; +import { Tag, type TagProps } from "./Tag"; +import { TAG_COLORS, TAG_VARIANTS } from "./constants"; +import type { StoryFn, Meta } from "@storybook/react-vite"; +import { CheckmarkIcon } from "../internal/icons"; + +type Args = Omit & { + startIcon?: boolean; + endIcon?: boolean; +}; + +const meta: Meta = { + title: "Components/Tag", + component: Tag, + argTypes: { + color: { + control: { + type: "select", + }, + options: TAG_COLORS, + }, + variant: { + control: { + type: "select", + }, + options: TAG_VARIANTS, + }, + startIcon: { + control: { + type: "boolean", + }, + }, + endIcon: { + control: { + type: "boolean", + }, + }, + }, + tags: ["autodocs"], +}; + +export default meta; + +function TagRenderer(args: Args) { + return ( + : undefined} + endIcon={args.endIcon ? : undefined} + /> + ); +} + +const Template: StoryFn = (args) => ; + +export const Default = Template.bind({}); + +Default.args = { + label: "Text", + startIcon: false, + endIcon: false, +}; + +const AllVariantsAndColorsTemplate: StoryFn> = ( + args +) => ( + <> + {TAG_VARIANTS.map((variant) => ( + + {TAG_COLORS.map((color) => ( + + + + ))} + + ))} + +); + +export const AllVariantsAndColors = AllVariantsAndColorsTemplate.bind({}); + +AllVariantsAndColors.args = { + label: "Text", + startIcon: false, + endIcon: false, +}; + +AllVariantsAndColors.parameters = { + controls: { + exclude: ["variant", "color"], + }, +}; diff --git a/packages/ui/src/Tag/Tag.tsx b/packages/ui/src/Tag/Tag.tsx new file mode 100644 index 00000000..707658f7 --- /dev/null +++ b/packages/ui/src/Tag/Tag.tsx @@ -0,0 +1,140 @@ +import { Box } from "@mui/material"; +import { ColorDynamic } from "../color"; +import { Typography } from "../Typography"; +import type { TAG_COLORS, TAG_VARIANTS } from "./constants"; + +export type TagColor = (typeof TAG_COLORS)[number]; +export type TagVariant = (typeof TAG_VARIANTS)[number]; + +export interface TagProps { + label: string; + color?: TagColor; + variant?: TagVariant; + isBold?: boolean; + startIcon?: React.ReactNode; + endIcon?: React.ReactNode; +} + +const variantToColorMap: Record< + TagVariant, + Record< + TagColor, + { + color: string; + backgroundColor: string; + } + > +> = { + filled: { + blue: { + color: ColorDynamic.White, + backgroundColor: ColorDynamic.Blue300, + }, + teal: { + color: ColorDynamic.White, + backgroundColor: ColorDynamic.Teal300, + }, + gray: { + color: ColorDynamic.White, + backgroundColor: ColorDynamic.Dark300, + }, + green: { + color: ColorDynamic.White, + backgroundColor: ColorDynamic.Green300, + }, + purple: { + color: ColorDynamic.White, + backgroundColor: ColorDynamic.Purple300, + }, + red: { + color: ColorDynamic.White, + backgroundColor: ColorDynamic.Red300, + }, + yellow: { + color: ColorDynamic.White, + backgroundColor: ColorDynamic.Yellow300, + }, + }, + subtle: { + blue: { + color: ColorDynamic.Blue500, + backgroundColor: ColorDynamic.Blue50, + }, + teal: { + color: ColorDynamic.Teal500, + backgroundColor: ColorDynamic.Teal50, + }, + gray: { + color: ColorDynamic.Dark300, + backgroundColor: ColorDynamic.Silver200, + }, + green: { + color: ColorDynamic.Green500, + backgroundColor: ColorDynamic.Green50, + }, + purple: { + color: ColorDynamic.Purple500, + backgroundColor: ColorDynamic.Purple50, + }, + red: { + color: ColorDynamic.Red500, + backgroundColor: ColorDynamic.Red50, + }, + yellow: { + color: ColorDynamic.Yellow500, + backgroundColor: ColorDynamic.Yellow50, + }, + }, +}; + +export function Tag({ + label, + variant = "subtle", + color = "blue", + isBold = true, + startIcon, + endIcon, +}: TagProps) { + const colorMap = variantToColorMap[variant][color]; + + return ( + ({ + color: colorMap.color, + backgroundColor: colorMap.backgroundColor, + borderRadius: "4px", + height: "22px", + paddingLeft: theme.spacing(0.5), + paddingRight: theme.spacing(0.5), + display: "inline-flex", + flexWrap: "nowrap", + whiteSpace: "nowrap", + alignItems: "center", + gap: theme.spacing(0.5), + + [theme.breakpoints.only("xs")]: { + height: "26px", + }, + })} + > + {startIcon && {startIcon}} + + + {label} + + + {endIcon && {endIcon}} + + ); +} + +function TagIcon({ children }: { children: React.ReactNode }) { + return ( + :nth-of-type(1)": { fontSize: "16px" } }} + > + {children} + + ); +} diff --git a/packages/ui/src/Tag/constants.ts b/packages/ui/src/Tag/constants.ts new file mode 100644 index 00000000..b92515d2 --- /dev/null +++ b/packages/ui/src/Tag/constants.ts @@ -0,0 +1,11 @@ +export const TAG_COLORS = [ + "blue", + "gray", + "green", + "purple", + "red", + "teal", + "yellow", +] as const; + +export const TAG_VARIANTS = ["filled", "subtle"] as const; diff --git a/packages/ui/src/Tag/index.ts b/packages/ui/src/Tag/index.ts new file mode 100644 index 00000000..c5a66e12 --- /dev/null +++ b/packages/ui/src/Tag/index.ts @@ -0,0 +1 @@ +export { Tag, type TagProps, type TagColor, type TagVariant } from "./Tag"; diff --git a/packages/ui/src/Typography/typographyOptions.tsx b/packages/ui/src/Typography/typographyOptions.tsx index 6a56f0b8..e50e7e3c 100644 --- a/packages/ui/src/Typography/typographyOptions.tsx +++ b/packages/ui/src/Typography/typographyOptions.tsx @@ -4,7 +4,7 @@ import type { } from "@mui/material/styles"; export function createTypographyOptions( - breakpoints: Breakpoints, + breakpoints: Breakpoints ): TypographyVariantsOptions { const xsOnly = breakpoints.only("xs"); @@ -80,8 +80,8 @@ export function createTypographyOptions( body: { fontSize: "14px", - lineHeight: "20px", - fontWeight: 600, + lineHeight: "22px", + fontWeight: 400, [xsOnly]: { fontSize: "16px", lineHeight: "26px", @@ -90,8 +90,8 @@ export function createTypographyOptions( "body-semibold": { fontSize: "14px", - lineHeight: "20px", - fontWeight: 400, + lineHeight: "22px", + fontWeight: 600, [xsOnly]: { fontSize: "16px", lineHeight: "26px", diff --git a/packages/ui/src/internal/icons.tsx b/packages/ui/src/internal/icons.tsx new file mode 100644 index 00000000..b436fd58 --- /dev/null +++ b/packages/ui/src/internal/icons.tsx @@ -0,0 +1,18 @@ +import { SvgIcon, type SvgIconProps } from "@mui/material"; + +export const CheckmarkIcon = (props: SvgIconProps) => { + return ( + + + + + ); +};