diff --git a/src/components/Badge/Badge.stories.tsx b/src/components/Badge/Badge.stories.tsx new file mode 100644 index 00000000..c04bf09a --- /dev/null +++ b/src/components/Badge/Badge.stories.tsx @@ -0,0 +1,78 @@ +// This file is part of MinIO Design System +// Copyright (c) 2023 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React from "react"; +import { Meta, Story } from "@storybook/react"; + +import Badge from "./Badge"; +import { BadgeProps } from "./Badge.types"; + +import StoryThemeProvider from "../../utils/StoryThemeProvider"; +import GlobalStyles from "../GlobalStyles/GlobalStyles"; +import DownloadIcon from "../Icons/DownloadIcon"; + +export default { + title: "MDS/Information/Badge", + component: Badge, + argTypes: {}, +} as Meta; + +const Template: Story = (args) => ( + + + alert("You clicked me!")}> + + + +); + +export const Default = Template.bind({}); +Default.args = { + badgeContent: 5, +}; + +export const DotOnly = Template.bind({}); +DotOnly.args = { + badgeContent: 5, + dotOnly: true, +}; + +export const Warn = Template.bind({}); +Warn.args = { badgeContent: 5, color: "warn" }; + +export const Secondary = Template.bind({}); +Secondary.args = { + badgeContent: 5, + color: "secondary", +}; + +export const Alert = Template.bind({}); +Alert.args = { + badgeContent: 5, + color: "alert", +}; + +export const Ok = Template.bind({}); +Ok.args = { + badgeContent: 5, + color: "ok", +}; + +export const Grey = Template.bind({}); +Grey.args = { + badgeContent: 5, + color: "grey", +}; diff --git a/src/components/Badge/Badge.tsx b/src/components/Badge/Badge.tsx new file mode 100644 index 00000000..c3417177 --- /dev/null +++ b/src/components/Badge/Badge.tsx @@ -0,0 +1,114 @@ +// This file is part of MinIO Design System +// Copyright (c) 2023 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { FC, Fragment, HTMLAttributes } from "react"; +import styled from "styled-components"; +import { BadgeConstruct, BadgeProps } from "./Badge.types"; +import { lightColors } from "../../global/themes"; +import get from "lodash/get"; + +const BadgeParent = styled.span< + HTMLAttributes & BadgeConstruct +>( + ({ + theme, + sx, + verticalPosition, + horizontalPosition, + color, + shape, + dotOnly, + }) => { + let circularRadius = dotOnly ? "100%" : 10; + + return { + position: "relative", + display: "inline-flex", + "& .badgeContent": { + fontSize: 10, + userSelect: "none", + display: "flex", + alignItems: "center", + justifyContent: "center", + position: "absolute", + padding: dotOnly ? 0 : "0 6px", + borderRadius: shape === "circular" ? circularRadius : 3, + right: horizontalPosition === "right" ? 0 : "initial", + top: verticalPosition === "top" ? 0 : "initial", + left: horizontalPosition === "left" ? 0 : "initial", + bottom: verticalPosition === "bottom" ? 0 : "initial", + minWidth: !dotOnly ? 20 : 10, + width: dotOnly ? 10 : "initial", + height: !dotOnly ? 20 : 10, + backgroundColor: get( + theme, + `badge.${color}.backgroundColor`, + lightColors.mainBlue, + ), + color: get(theme, `badge.${color}.textColor`, lightColors.white), + fontWeight: "bold", + textAlign: "center", + zIndex: 1, + transform: `scale(1) translate(${ + horizontalPosition === "right" ? "" : "-" + }50%, ${verticalPosition === "bottom" ? "" : "-"}50%)`, + }, + ...sx, + }; + }, +); + +const Badge: FC & BadgeProps> = ({ + sx, + children, + horizontalPosition = "right" as "right" | "left", + verticalPosition = "bottom" as "top" | "bottom", + color = "default", + shape = "circular", + dotOnly = false, + invisible = false, + max = 99, + showZero = false, + badgeContent = 0, + ...props +}) => { + return ( + + {children} + {!invisible && + (badgeContent >= 0 || (showZero && badgeContent === 0)) && ( +
+ {!dotOnly ? ( + + {badgeContent > max ? `${max}+` : badgeContent} + + ) : ( + "" + )} +
+ )} +
+ ); +}; +export default Badge; diff --git a/src/components/Badge/Badge.types.ts b/src/components/Badge/Badge.types.ts new file mode 100644 index 00000000..cfa808c3 --- /dev/null +++ b/src/components/Badge/Badge.types.ts @@ -0,0 +1,35 @@ +// This file is part of MinIO Design System +// Copyright (c) 2023 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { CSSObject } from "styled-components"; + +export interface BadgeMain { + invisible?: boolean; + max?: number; + showZero?: boolean; + badgeContent?: number; +} + +export interface BadgeConstruct { + horizontalPosition?: "left" | "right"; + verticalPosition?: "bottom" | "top"; + sx?: CSSObject; + color?: "default" | "secondary" | "warn" | "alert" | "ok" | "grey"; + shape?: "circular" | "rectangular"; + dotOnly?: boolean; +} + +export type BadgeProps = BadgeMain & BadgeConstruct; diff --git a/src/components/index.ts b/src/components/index.ts index ae7b1bf9..ccd02c34 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -66,6 +66,7 @@ export { default as Snackbar } from "./Snackbar/Snackbar"; export { default as Accordion } from "./Accordion/Accordion"; export { default as HelpTip } from "./HelpTip/HelpTip"; export { default as Autocomplete } from "./Autocomplete/Autocomplete"; +export { default as Badge } from "./Badge/Badge"; /*Icons*/ export * from "./Icons"; @@ -115,3 +116,4 @@ export * from "./Snackbar/Snackbar.types"; export * from "./Accordion/Accordion.types"; export * from "./HelpTip/HelpTip.types"; export * from "./Autocomplete/Autocomplete.types"; +export * from "./Badge/Badge.types"; diff --git a/src/global/global.types.ts b/src/global/global.types.ts index 80beea0f..1605ce7d 100644 --- a/src/global/global.types.ts +++ b/src/global/global.types.ts @@ -15,6 +15,7 @@ // along with this program. If not, see . import React from "react"; +import { lightColors } from "./themes"; export interface ButtonThemeProps { border: string; @@ -295,6 +296,20 @@ export interface InformativeMessageProps { error: InformativeColorElements; } +export interface BadgeColorElements { + backgroundColor: string; + textColor: string; +} + +export interface BadgeStyleProps { + alert: BadgeColorElements; + default: BadgeColorElements; + secondary: BadgeColorElements; + warn: BadgeColorElements; + ok: BadgeColorElements; + grey: BadgeColorElements; +} + export interface ThemeDefinitionProps { bgColor: string; fontColor: string; @@ -337,6 +352,7 @@ export interface ThemeDefinitionProps { tag?: TagThemeProps; snackbar?: SnackBarThemeProps; informativeMessage?: InformativeMessageProps; + badge?: BadgeStyleProps; } export interface SelectorType { diff --git a/src/global/themes.ts b/src/global/themes.ts index 9aceee05..9c1566e4 100644 --- a/src/global/themes.ts +++ b/src/global/themes.ts @@ -606,6 +606,32 @@ export const lightTheme: ThemeDefinitionProps = { textColor: lightColors.defaultFontColor, }, }, + badge: { + alert: { + backgroundColor: lightColors.mainRed, + textColor: lightColors.white, + }, + default: { + backgroundColor: lightColors.mainBlue, + textColor: lightColors.white, + }, + secondary: { + backgroundColor: lightColors.secondAction, + textColor: lightColors.white, + }, + warn: { + backgroundColor: lightColors.mainOrange, + textColor: lightColors.defaultFontColor, + }, + ok: { + backgroundColor: lightColors.mainGreen, + textColor: lightColors.defaultFontColor, + }, + grey: { + backgroundColor: lightColors.actionDisabledGrey, + textColor: lightColors.defaultFontColor, + }, + }, }; export const darkTheme: ThemeDefinitionProps = { @@ -1032,4 +1058,30 @@ export const darkTheme: ThemeDefinitionProps = { textColor: darkColors.dark, }, }, + badge: { + alert: { + backgroundColor: darkColors.mainRed, + textColor: darkColors.mainWhite, + }, + default: { + backgroundColor: darkColors.mainGrey, + textColor: darkColors.dark, + }, + secondary: { + backgroundColor: darkColors.secondAction, + textColor: darkColors.mainWhite, + }, + warn: { + backgroundColor: darkColors.mainOrange, + textColor: darkColors.dark, + }, + ok: { + backgroundColor: darkColors.mainGreen, + textColor: darkColors.dark, + }, + grey: { + backgroundColor: darkColors.disabledBGGrey, + textColor: darkColors.mainWhite, + }, + }, };