aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
275 lines (272 loc) • 8.83 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { forwardRef, useState } from 'react';
import '../../../primitives/GlassCore.js';
import '../../../primitives/glass/GlassAdvanced.js';
import '../../../primitives/OptimizedGlassCore.js';
import '../../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../../primitives/MotionNative.js';
import { MotionFramer } from '../../../primitives/motion/MotionFramer.js';
import { GlassFormBuilder } from '../../interactive/GlassFormBuilder.js';
import { GlassButton } from '../../button/GlassButton.js';
import { GlassProgress } from '../../data-display/GlassProgress.js';
import { PageHeader } from '../../layout/GlassAppShell.js';
import { VStack, HStack } from '../../layout/GlassStack.js';
import { GlassCard } from '../../card/GlassCard.js';
import { cn } from '../../../lib/utilsComprehensive.js';
/**
* GlassFormTemplate component
* Comprehensive form template with single-step and multi-step support
*/
const GlassFormTemplate = /*#__PURE__*/forwardRef(({
title,
description,
steps = [],
schema = [],
values = {},
errors = {},
multiStep = false,
currentStep = 0,
onStepChange,
onSubmit,
onCancel,
onChange,
onValidate,
loading = false,
allowDraft = false,
onSaveDraft,
layout = "default",
sidebar,
showProgress = true,
autoSave = false,
submitText = "Submit",
cancelText = "Cancel",
nextText = "Next",
previousText = "Previous",
className,
...props
}, ref) => {
const [internalValues, setInternalValues] = useState(values);
const [internalErrors, setInternalErrors] = useState(errors);
const [stepValidation, setStepValidation] = useState({});
const isMultiStep = multiStep && (steps?.length || 0) > 0;
const totalSteps = isMultiStep ? steps?.length || 0 : 1;
const currentStepData = isMultiStep ? steps[currentStep] : null;
const currentSchema = isMultiStep ? currentStepData?.sections : schema;
// Handle value change
const handleValueChange = newValues => {
setInternalValues(newValues);
onChange?.(newValues);
};
// Handle form validation
const handleValidate = async stepValues => {
let errors = {};
// Global validation
if (onValidate) {
errors = {
...errors,
...onValidate(stepValues)
};
}
// Step-specific validation
if (currentStepData?.validation) {
const stepErrors = await currentStepData.validation(stepValues);
errors = {
...errors,
...stepErrors
};
}
setInternalErrors(errors);
return errors;
};
// Handle step navigation
const handleNext = async () => {
if (!isMultiStep || currentStep >= totalSteps - 1) return;
const errors = await handleValidate(internalValues);
const hasErrors = Object.keys(errors).length > 0;
if (!hasErrors) {
setStepValidation({
...stepValidation,
[currentStep]: true
});
onStepChange?.(currentStep + 1);
}
};
const handlePrevious = () => {
if (!isMultiStep || currentStep <= 0) return;
onStepChange?.(currentStep - 1);
};
// Handle form submission
const handleSubmit = async formValues => {
if (isMultiStep) {
const errors = await handleValidate(formValues);
const hasErrors = Object.keys(errors).length > 0;
if (hasErrors) return;
if (currentStep < totalSteps - 1) {
handleNext();
return;
}
}
await onSubmit?.(formValues);
};
// Handle draft save
const handleSaveDraft = () => {
onSaveDraft?.(internalValues);
};
// Calculate progress
const getProgress = () => {
if (!isMultiStep) return 100;
return (currentStep + 1) / totalSteps * 100;
};
// Render step indicator
const renderStepIndicator = () => {
if (!isMultiStep || !showProgress) return null;
return jsxs(VStack, {
"data-glass-component": true,
space: "md",
children: [jsxs(HStack, {
space: "sm",
align: "center",
justify: "between",
children: [jsxs("span", {
className: 'glass-text-sm font-medium text-primary',
children: ["Step ", currentStep + 1, " of ", totalSteps]
}), jsxs("span", {
className: "glass-text-sm glass-text-secondary",
children: [Math.round(getProgress()), "% Complete"]
})]
}), jsx(GlassProgress, {
value: getProgress(),
size: "sm",
variant: "default",
showValue: false
}), jsx("div", {
className: "glass-flex glass-justify-between",
children: steps.map((step, index) => jsxs("div", {
className: cn("flex flex-col items-center glass-gap-2 cursor-pointer transition-opacity", index > currentStep && "opacity-50", index < currentStep && "opacity-75"),
onClick: e => stepValidation[index] && onStepChange?.(index),
children: [jsx("div", {
className: cn("w-8 h-8 glass-radius-full flex items-center justify-center glass-text-sm font-medium", index === currentStep ? "bg-primary text-primary-foreground" : index < currentStep ? "bg-success text-success-foreground" : "bg-muted glass-text-secondary"),
children: index < currentStep ? "✓" : index + 1
}), jsx("span", {
className: 'glass-text-xs text-center max-w-16 truncate',
children: step.title
})]
}, step.id))
})]
});
};
// Render form actions
const renderFormActions = () => jsxs(HStack, {
space: "sm",
align: "center",
justify: "between",
children: [jsxs(HStack, {
space: "sm",
children: [onCancel && jsx(GlassButton, {
variant: "ghost",
onClick: onCancel,
disabled: loading,
children: cancelText
}), allowDraft && jsx(GlassButton, {
variant: "outline",
onClick: handleSaveDraft,
disabled: loading,
children: "Save Draft"
})]
}), jsxs(HStack, {
space: "sm",
children: [isMultiStep && currentStep > 0 && jsx(GlassButton, {
variant: "ghost",
onClick: handlePrevious,
disabled: loading,
children: previousText
}), jsx(GlassButton, {
variant: "default",
type: "submit",
loading: loading,
children: isMultiStep && currentStep < totalSteps - 1 ? nextText : submitText
})]
})]
});
// Render form content
const renderFormContent = () => jsxs(VStack, {
space: "lg",
children: [renderStepIndicator(), isMultiStep && currentStepData && jsxs(VStack, {
space: "sm",
children: [jsx("h2", {
className: 'glass-text-xl font-semibold text-primary',
children: currentStepData.title
}), currentStepData.description && jsx("p", {
className: "glass-text-secondary",
children: currentStepData.description
})]
}), jsx("div", {
className: "glass-flex-1",
children: jsx(GlassFormBuilder, {
schema: currentSchema || [],
values: internalValues,
errors: internalErrors,
onChange: handleValueChange,
onSubmit: handleSubmit,
onValidate: onValidate,
validateOnChange: true,
autoSave: autoSave,
showProgress: false,
submitText: "",
showCancel: false
})
}), renderFormActions()]
});
// Render layout
const renderLayout = () => {
switch (layout) {
case "centered":
return jsx("div", {
className: 'max-w-2xl glass-mx-auto',
children: jsx(GlassCard, {
variant: "default",
className: "glass-p-8",
children: renderFormContent()
})
});
case "sidebar":
return jsxs("div", {
className: "glass-grid glass-grid-cols-12 glass-gap-8",
children: [jsx("div", {
className: 'col-span-8',
children: jsx(GlassCard, {
variant: "default",
className: "glass-p-6",
children: renderFormContent()
})
}), jsx("div", {
className: 'col-span-4',
children: sidebar
})]
});
default:
return jsx(GlassCard, {
variant: "default",
className: "glass-p-6",
children: renderFormContent()
});
}
};
return jsxs("div", {
ref: ref,
className: cn("w-full glass-auto-gap glass-auto-gap-3xl", className),
...props,
children: [jsx(PageHeader, {
title: title,
description: description,
variant: layout === "centered" ? "centered" : "default"
}), jsx(MotionFramer, {
preset: "fadeIn",
children: renderLayout()
})]
});
});
GlassFormTemplate.displayName = "GlassFormTemplate";
export { GlassFormTemplate };
//# sourceMappingURL=GlassFormTemplate.js.map