Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
|
|
@ -0,0 +1,202 @@
|
|||
// File Path: web/frontend/src/components/UIX/CreateButton/CreateButton.jsx
|
||||
// CreateButton Component - Performance Optimized
|
||||
|
||||
import React, { memo, useMemo } from "react";
|
||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||
import { useUIXTheme } from "../themes/useUIXTheme.jsx";
|
||||
|
||||
/**
|
||||
* CreateButton Component - Performance Optimized
|
||||
* Green-themed button specifically for creation actions
|
||||
*
|
||||
* @param {React.ReactNode} children - Button text content
|
||||
* @param {Function} onClick - Click handler function
|
||||
* @param {boolean} disabled - Whether button is disabled
|
||||
* @param {string} type - Button type (button, submit, reset)
|
||||
* @param {string} className - Additional CSS classes
|
||||
* @param {boolean} loading - Loading state
|
||||
* @param {string} loadingText - Text to show when loading
|
||||
* @param {boolean} fullWidth - Whether button should take full width
|
||||
* @param {string} size - Button size (sm, md, lg, xl)
|
||||
* @param {React.ComponentType} icon - Icon component (defaults to PlusIcon)
|
||||
* @param {boolean} gradient - Whether to use gradient background
|
||||
*/
|
||||
|
||||
// Static size classes - moved outside to prevent recreation
|
||||
const SIZE_CLASSES = Object.freeze({
|
||||
sm: "px-3 py-2 text-xs sm:text-sm",
|
||||
md: "px-4 py-3 text-sm sm:text-base",
|
||||
lg: "px-6 sm:px-8 py-3 sm:py-4 text-sm sm:text-base",
|
||||
xl: "px-8 py-4 text-base sm:text-lg",
|
||||
});
|
||||
|
||||
// Loading Spinner Component - Separated for better performance
|
||||
const LoadingSpinner = memo(function LoadingSpinner() {
|
||||
return (
|
||||
<svg
|
||||
className="animate-spin -ml-1 mr-2 h-4 w-4 text-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
/>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
});
|
||||
|
||||
LoadingSpinner.displayName = "LoadingSpinner";
|
||||
|
||||
const CreateButton = memo(
|
||||
function CreateButton({
|
||||
children,
|
||||
onClick,
|
||||
disabled = false,
|
||||
type = "button",
|
||||
className = "",
|
||||
loading = false,
|
||||
loadingText,
|
||||
fullWidth = false,
|
||||
size = "lg",
|
||||
icon: Icon = PlusIcon,
|
||||
gradient = false,
|
||||
}) {
|
||||
const { getThemeClasses } = useUIXTheme();
|
||||
|
||||
// Computed disabled state
|
||||
const isDisabled = disabled || loading;
|
||||
|
||||
// Memoize theme classes
|
||||
const themeClasses = useMemo(
|
||||
() => ({
|
||||
buttonCreate: getThemeClasses("button-create"),
|
||||
inputFocusRing: getThemeClasses("input-focus-ring"),
|
||||
bgGradientSecondary: getThemeClasses("bg-gradient-secondary"),
|
||||
}),
|
||||
[getThemeClasses],
|
||||
);
|
||||
|
||||
// Memoize gradient style
|
||||
const gradientStyle = useMemo(() => {
|
||||
if (!gradient) return {};
|
||||
|
||||
return {
|
||||
background:
|
||||
themeClasses.bgGradientSecondary ||
|
||||
"linear-gradient(to right, #059669, #10b981)",
|
||||
};
|
||||
}, [gradient, themeClasses.bgGradientSecondary]);
|
||||
|
||||
// Memoize button classes
|
||||
const buttonClasses = useMemo(() => {
|
||||
const classes = [
|
||||
SIZE_CLASSES[size] || SIZE_CLASSES.lg,
|
||||
"font-medium",
|
||||
"rounded-xl",
|
||||
"focus:outline-none",
|
||||
"transition-all",
|
||||
"duration-200",
|
||||
"inline-flex",
|
||||
"items-center",
|
||||
"justify-center",
|
||||
];
|
||||
|
||||
if (fullWidth) {
|
||||
classes.push("w-full");
|
||||
}
|
||||
|
||||
// Variant classes
|
||||
if (gradient) {
|
||||
classes.push(
|
||||
"border-transparent",
|
||||
"text-white",
|
||||
"shadow-lg",
|
||||
"hover:shadow-xl",
|
||||
themeClasses.inputFocusRing,
|
||||
"transform",
|
||||
"hover:scale-105",
|
||||
"font-bold",
|
||||
);
|
||||
} else {
|
||||
classes.push(themeClasses.buttonCreate);
|
||||
}
|
||||
|
||||
// State classes
|
||||
if (isDisabled) {
|
||||
classes.push("opacity-50", "cursor-not-allowed");
|
||||
} else {
|
||||
classes.push("cursor-pointer");
|
||||
}
|
||||
|
||||
// Custom className
|
||||
if (className) {
|
||||
classes.push(className);
|
||||
}
|
||||
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}, [size, fullWidth, gradient, themeClasses, isDisabled, className]);
|
||||
|
||||
// Memoize button content
|
||||
const ButtonContent = useMemo(() => {
|
||||
if (loading) {
|
||||
return (
|
||||
<>
|
||||
<LoadingSpinner />
|
||||
{loadingText || "Creating..."}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{Icon && <Icon className="w-4 h-4 mr-2" />}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}, [loading, loadingText, Icon, children]);
|
||||
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
className={buttonClasses}
|
||||
onClick={isDisabled ? undefined : onClick}
|
||||
disabled={isDisabled}
|
||||
style={gradientStyle}
|
||||
>
|
||||
{ButtonContent}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
(prevProps, nextProps) => {
|
||||
// Custom comparison function - only re-render when these props actually change
|
||||
return (
|
||||
prevProps.children === nextProps.children &&
|
||||
prevProps.onClick === nextProps.onClick &&
|
||||
prevProps.disabled === nextProps.disabled &&
|
||||
prevProps.type === nextProps.type &&
|
||||
prevProps.className === nextProps.className &&
|
||||
prevProps.loading === nextProps.loading &&
|
||||
prevProps.loadingText === nextProps.loadingText &&
|
||||
prevProps.fullWidth === nextProps.fullWidth &&
|
||||
prevProps.size === nextProps.size &&
|
||||
prevProps.icon === nextProps.icon &&
|
||||
prevProps.gradient === nextProps.gradient
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Display name for debugging
|
||||
CreateButton.displayName = "CreateButton";
|
||||
|
||||
export default CreateButton;
|
||||
Loading…
Add table
Add a link
Reference in a new issue