175 lines
4.2 KiB
React
175 lines
4.2 KiB
React
import PropTypes from "prop-types";
|
|
import { Box } from "@mui/material";
|
|
import { useTheme } from "@mui/material";
|
|
import "./index.css";
|
|
|
|
/**
|
|
* @typedef {Object} Styles
|
|
* @param {string} [color] - The text color
|
|
* @param {string} [backgroundColor] - The background color
|
|
* @param {string} [borderColor] - The border color
|
|
*/
|
|
|
|
/**
|
|
* @component
|
|
* @param {Object} props
|
|
* @param {string} props.label - The label of the label
|
|
* @param {Styles} props.styles - CSS Styles passed from parent component
|
|
* @param {React.ReactNode} children - Children passed from parent component
|
|
* @returns {JSX.Element}
|
|
*/
|
|
|
|
const BaseLabel = ({ label, styles, children }) => {
|
|
const theme = useTheme();
|
|
// Grab the default borderRadius from the theme to match button style
|
|
const { borderRadius } = theme.shape;
|
|
// Calculate padding for the label to mimic button. Appears to scale correctly, not 100% sure though.
|
|
const padding = theme.spacing(3, 5);
|
|
|
|
return (
|
|
<Box
|
|
className="label"
|
|
sx={{
|
|
borderRadius: borderRadius,
|
|
border: `1px solid ${theme.palette.primary.lowContrast}`,
|
|
color: theme.palette.primary.contrastText,
|
|
padding: padding,
|
|
...styles,
|
|
}}
|
|
>
|
|
{children}
|
|
{label}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
BaseLabel.propTypes = {
|
|
label: PropTypes.string.isRequired,
|
|
styles: PropTypes.shape({
|
|
color: PropTypes.string,
|
|
backgroundColor: PropTypes.string,
|
|
}),
|
|
children: PropTypes.node,
|
|
};
|
|
|
|
// Produces a lighter color based on a hex color and a percent
|
|
// lightenColor("#067647", 20) will produce a color 20% lighter than #067647
|
|
const lightenColor = (color, percent) => {
|
|
let r = parseInt(color.substring(1, 3), 16);
|
|
let g = parseInt(color.substring(3, 5), 16);
|
|
let b = parseInt(color.substring(5, 7), 16);
|
|
|
|
const amt = Math.round((255 * percent) / 100);
|
|
|
|
r = r + amt <= 255 ? r + amt : 255;
|
|
g = g + amt <= 255 ? g + amt : 255;
|
|
b = b + amt <= 255 ? b + amt : 255;
|
|
|
|
r = r.toString(16).padStart(2, "0");
|
|
g = g.toString(16).padStart(2, "0");
|
|
b = b.toString(16).padStart(2, "0");
|
|
|
|
return `#${r}${g}${b}`;
|
|
};
|
|
|
|
/**
|
|
* @component
|
|
* @param {Object} props
|
|
* @param {string} props.label - The label of the label
|
|
* @param {string} props.color - The color of the label, specified in #RRGGBB format
|
|
* @returns {JSX.Element}
|
|
* @example
|
|
* // Render a red label
|
|
* <ColoredLabel label="Label" color="#FF0000" />
|
|
*/
|
|
|
|
const ColoredLabel = ({ label, color }) => {
|
|
const theme = useTheme();
|
|
// If an invalid color is passed, default to the labelGray color
|
|
if (typeof color !== "string" || !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color)) {
|
|
color = theme.palette.primary.lowContrast;
|
|
}
|
|
|
|
// Calculate lighter shades for border and bg
|
|
const borderColor = lightenColor(color, 20);
|
|
const bgColor = lightenColor(color, 75);
|
|
|
|
return (
|
|
<BaseLabel
|
|
label={label}
|
|
styles={{
|
|
color: color,
|
|
borderColor: borderColor,
|
|
backgroundColor: bgColor,
|
|
}}
|
|
></BaseLabel>
|
|
);
|
|
};
|
|
|
|
ColoredLabel.propTypes = {
|
|
label: PropTypes.string.isRequired,
|
|
color: PropTypes.string.isRequired,
|
|
};
|
|
|
|
/**
|
|
* @component
|
|
* @param {Object} props
|
|
* @param {'up' | 'down' | 'paused' | 'pending' | 'cannot resolve' | 'published' | 'unpublished'} props.status - The status for the label
|
|
* @param {string} props.text - The text of the label
|
|
* @returns {JSX.Element}
|
|
* @example
|
|
* // Render an active label
|
|
* <StatusLabel status="up" text="Active" />
|
|
*/
|
|
|
|
const statusToTheme = {
|
|
up: "success",
|
|
down: "error",
|
|
paused: "warning",
|
|
pending: "warning",
|
|
"cannot resolve": "error",
|
|
published: "success",
|
|
unpublished: "error",
|
|
};
|
|
|
|
const StatusLabel = ({ status, text, customStyles }) => {
|
|
const theme = useTheme();
|
|
|
|
const themeColor = statusToTheme[status];
|
|
|
|
return (
|
|
<BaseLabel
|
|
label={text}
|
|
styles={{
|
|
color: theme.palette[themeColor].main,
|
|
borderColor: theme.palette[themeColor].lowContrast,
|
|
...customStyles,
|
|
}}
|
|
>
|
|
<Box
|
|
width={7}
|
|
height={7}
|
|
bgcolor={theme.palette[themeColor].lowContrast}
|
|
borderRadius="50%"
|
|
marginRight="5px"
|
|
/>
|
|
</BaseLabel>
|
|
);
|
|
};
|
|
|
|
StatusLabel.propTypes = {
|
|
status: PropTypes.oneOf([
|
|
"up",
|
|
"down",
|
|
"paused",
|
|
"pending",
|
|
"cannot resolve",
|
|
"published",
|
|
"unpublished",
|
|
]),
|
|
text: PropTypes.string,
|
|
customStyles: PropTypes.object,
|
|
};
|
|
|
|
export { BaseLabel, ColoredLabel, StatusLabel };
|