Merge pull request #144 from bluewave-labs/feat/loading-spinners

Implemented Loading Button Spinner Component, #142
This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-06-15 20:11:06 -07:00
commit 9da39c8f91
4 files changed
+210 -2

No files matched your search

@@ -0,0 +1,105 @@
import LoadingButton from "@mui/lab/LoadingButton";
import PropTypes from "prop-types";
const levelConfig = {
primary: {
variant: "contained",
color: "primary",
},
secondary: {
variant: "outlined",
color: "secondary",
},
tertiary: {
variant: "text",
color: "tertiary",
},
error: {
variant: "contained",
color: "error",
},
imagePrimary: {
color: "primary",
variant: "contained",
},
imageSecondary: {
color: "secondary",
variant: "outlined",
},
imageTertiary: {
color: "tertiary",
variant: "text",
},
};
/**
* ButtonSpinner component displays a button with loading loadingText capability.
* @component
* @param {Object} props
* @param {'primary' | 'secondary' | 'tertiary' | 'error' | 'imagePrimary' | 'imageSecondary' | 'imageTertiary'} props.level - The style level of the button.
* @param {string} props.label - The label text displayed on the button.
* @param {React.ReactNode} [props.img] - Icon or image element to display within the button.
* @param {boolean} [props.disabled=false] - Determines if the button is disabled.
* @param {string} [props.loadingText] - Text displayed when the button is in a loading state.
* @param {'start' | 'end'} [props.position] - Specifies where the icon or loading loadingText should be positioned relative to the label.
* @param {function} props.onClick - Callback function invoked when the button is clicked.
* @param {boolean} props.isLoading - Indicates if the button is in a loading state.
* @param {Object} [props.sx] - Additional styles to apply to the button.
* @returns {JSX.Element}
* @example
* // Render a primary button with an icon at the end
* <ButtonSpinner
* level="primary"
* label="Save"
* img={<SaveIcon />}
* position="end"
* onClick={handleSaveClick}
* isLoading={loading}
* />
*/
const ButtonSpinner = ({
level,
label,
disabled,
loadingText,
position,
img,
onClick,
isLoading,
sx,
}) => {
const { variant, color } = levelConfig[level];
//if both a loadingPosition and loadingIndicator are provided, the spinner overlaps with the text and it breaks
if (position && loadingText) loadingText = null;
return (
<LoadingButton
variant={variant}
color={color}
disabled={disabled}
loadingIndicator={loadingText}
loadingPosition={position}
startIcon={position === "start" && img}
endIcon={position === "end" && img}
onClick={onClick}
loading={isLoading}
sx={{ textTransform: "none", ...sx }}
>
<span>{label}</span>
</LoadingButton>
);
};
ButtonSpinner.propTypes = {
level: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
loadingText: PropTypes.string,
position: PropTypes.oneOf(["start", "end"]),
img: PropTypes.node,
onClick: PropTypes.func.isRequired,
isLoading: PropTypes.bool.isRequired,
sx: PropTypes.object,
disabled: PropTypes.bool,
};
export default ButtonSpinner;
+63 -1
View File
@@ -1,5 +1,6 @@
import React from "react";
import React, { useState } from "react";
import Button from "../../Components/Button/";
import ButtonSpinner from "../../Components/ButtonSpinner";
import Link from "../../Components/Link";
import {
useTheme,
@@ -13,6 +14,7 @@ import {
Tab,
Tabs,
Typography,
Stack,
} from "@mui/material";
import { ColoredLabel, StatusLabel } from "../../Components/Label/";
import Avatar from "../../Components/Avatar/";
@@ -25,6 +27,8 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import AddIcon from "@mui/icons-material/Add";
import Divider from "@mui/material/Divider";
import UploadIcon from '@mui/icons-material/Upload';
import SendIcon from '@mui/icons-material/Send';
// Redux
import { useSelector } from "react-redux";
@@ -181,6 +185,16 @@ const Demo = () => {
// *********************************
const monitorState = useSelector((state) => state.monitors);
const { monitors, msg } = monitorState;
//Button Spinner state
const [isLoading, setIsLoading] = useState(false);
const handleButtonSpinnerClick = () => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false);
}, 4000);
};
return (
<div>
<div style={{ padding: "4rem", border: "1px solid black" }}>
@@ -352,6 +366,54 @@ const Demo = () => {
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
<h4 id="section">Section</h4>
<Section monitors={demoMonitors} />
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
<h4 id="section">Loading Buttons</h4>
<Stack direction="row" justifyContent="center" spacing={3}>
<ButtonSpinner
level="primary"
label="Submit"
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
/>
<ButtonSpinner
level="secondary"
label="Fetch Data"
loadingText="Fetching..."
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
/>
<ButtonSpinner
level="tertiary"
label="Add"
loadingText="Adding..."
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
/>
<ButtonSpinner
level="imagePrimary"
label="Upload"
position="start"
img={<UploadIcon/>}
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
/>
<ButtonSpinner
level="imageSecondary"
label="Send"
position="end"
img={<SendIcon/>}
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
/>
<ButtonSpinner
level="error"
label="Disabled"
onClick={handleButtonSpinnerClick}
isLoading={isLoading}
disabled={true}
/>
</Stack>
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
</div>
);
};