diff --git a/src/components/SizeChart/SizeChart.stories.tsx b/src/components/SizeChart/SizeChart.stories.tsx new file mode 100644 index 00000000..326076a4 --- /dev/null +++ b/src/components/SizeChart/SizeChart.stories.tsx @@ -0,0 +1,79 @@ +// 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, { Fragment } from "react"; +import { Meta, Story } from "@storybook/react"; + +import StoryThemeProvider from "../../../../../Downloads/mds-master 2/src/utils/StoryThemeProvider"; +import TestIcon from "../../../../../Downloads/mds-master 2/src/utils/TestIcon"; +import { Button, GlobalStyles } from "../../../../../Downloads/mds-master 2/src/components"; +import SizeChart from "./SizeChart"; +import { SizeChartProps } from "./SizeChart.types"; + +export default { + title: "MDS/Data/SizeChart", + component: SizeChart, + argTypes: {}, +} as Meta; + +const Template: Story = (args) => ( + + + + +); + +export const Default = Template.bind({}); +Default.args = { + usedBytes: 45000, + totalBytes: 100000, +}; + +export const WarningSpace = Template.bind({}); +WarningSpace.args = { + usedBytes: 85000, + totalBytes: 100000, +}; + +export const DangerSpace = Template.bind({}); +DangerSpace.args = { + usedBytes: 95000, + totalBytes: 100000, +}; + +export const WithLabel = Template.bind({}); +WithLabel.args = { + usedBytes: 95000, + totalBytes: 100000, + label: true, +}; + +export const WithChartLabel = Template.bind({}); +WithChartLabel.args = { + usedBytes: 9504400, + totalBytes: 103840000, + label: true, + chartLabel: "Reported Usage", +}; + +export const CustomSize = Template.bind({}); +CustomSize.args = { + usedBytes: 95000, + totalBytes: 100000, + label: true, + width: 50, + height: 50, +}; diff --git a/src/components/SizeChart/SizeChart.tsx b/src/components/SizeChart/SizeChart.tsx new file mode 100644 index 00000000..c3f3898a --- /dev/null +++ b/src/components/SizeChart/SizeChart.tsx @@ -0,0 +1,164 @@ +// 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, SVGProps, useEffect, useRef } from "react"; +import get from "lodash/get"; +import styled from "styled-components"; + +import { lightColors } from "../../../../../Downloads/mds-master 2/src/global/themes"; +import { calculateBytes, overridePropsParse } from "../../../../../Downloads/mds-master 2/src/global/utils"; +import { SizeChartConstructProps, SizeChartProps } from "./SizeChart.types"; + +const SizeChartBase = styled.svg & SizeChartConstructProps>( + ({ theme, usedBytes, totalBytes, chartLabel, sx }) => { + const percUsed = (usedBytes * 100) / totalBytes; + + let color = get(theme, "signalColors.main", lightColors.mainBlue); + + if (percUsed >= 90) { + color = get(theme, "signalColors.danger", lightColors.mainRed); + } else if (percUsed >= 80) { + color = get(theme, "signalColors.warning", lightColors.mainOrange); + } + + return { + "& .usedSpace": { + stroke: color, + }, + "& .availableSpace": { + stroke: get(theme, "signalColors.disabled", lightColors.disabledGrey), + }, + "& .chartText": { + fill: "#000", + transform: "translateY(0.25em)", + }, + "& .chartNumber": { + fontSize: "0.3em", + lineHeight: 1, + textAnchor: "middle", + transform: + chartLabel !== "" ? "translateY(-0.50em)" : "translateY(-0.25em)", + fontWeight: "bold", + fill: get(theme, "fontColor", lightColors.defaultFontColor), + }, + "& .chartLabel": { + fontSize: "0.2em", + textAnchor: "middle", + transform: "translateY(0.7em)", + whiteSpace: "normal", + fontWeight: "bold", + fill: get(theme, "mutedText", lightColors.mutedText), + }, + ...overridePropsParse(sx, theme), + }; + }, +); + +const SizeChart: FC = ({ + width = "150", + height = "150", + usedBytes, + totalBytes, + label, + chartLabel = "", + sx, +}) => { + const val1D = useRef(null); + const val2D = useRef(null); + + useEffect(() => { + setTimeout(function () { + if (val1D?.current) { + val1D.current.style.transition = + "stroke-dasharray 0.5s ease-in-out, stroke-dashoffset 0.5s ease-in-out"; + val1D.current.style.strokeDasharray = "100 0"; + } + }, 20); + }, []); + + useEffect(() => { + const val1 = usedBytes; + const val2 = totalBytes - usedBytes; + + const total = val1 + val2; + + const per1 = (val1 / total) * 100; + const per2 = (val2 / total) * 100; + const offset = 25; + + if (val1D?.current && val2D?.current) { + val1D.current.style.transition = + "stroke-dasharray 0.5s ease-in-out, stroke-dashoffset 0.5s ease-in-out"; + val1D.current.style.strokeDasharray = per1 + " " + (100 - per1); + val1D.current.style.strokeDashoffset = `${offset}`; + + val2D.current.style.transition = + "stroke-dasharray 0.5s ease-in-out, stroke-dashoffset 0.5s ease-in-out"; + val2D.current.style.strokeDasharray = per2 + " " + (100 - per2); + val2D.current.style.strokeDashoffset = `${100 - per1 + offset}`; + } + }, [usedBytes, totalBytes]); + + const usedSizeUnits = calculateBytes(usedBytes); + + return ( + + + + {label && ( + + + {usedSizeUnits.total} {usedSizeUnits.unit} + + + {chartLabel} + + + )} + + ); +}; + +export default SizeChart; diff --git a/src/components/SizeChart/SizeChart.types.ts b/src/components/SizeChart/SizeChart.types.ts new file mode 100644 index 00000000..95b2249b --- /dev/null +++ b/src/components/SizeChart/SizeChart.types.ts @@ -0,0 +1,32 @@ +// 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 { OverrideTheme } from "../../../../../Downloads/mds-master 2/src/global/global.types"; + +export interface SizeChartMain { + label: boolean; + width?: string; + height?: string; +} + +export interface SizeChartConstructProps { + usedBytes: number; + totalBytes: number; + chartLabel?: string; + sx?: OverrideTheme; +} + +export type SizeChartProps = SizeChartMain & SizeChartConstructProps;