import React, { useMemo } from 'react'; import { Button, XStack, YStack } from 'tamagui'; import { SidePanelShell } from './SidePanelShell.jsx'; import { FormField } from './FormField.jsx'; import { getIcon } from './IconMapper.jsx'; function defaultExpressionEvaluator(template, form) { return template.replace(/\{\{(\w+)\}\}/g, (_match, fieldName) => form[fieldName] || ''); } function renderAction(action, index, fallbackHandler) { const IconComponent = action?.icon ? getIcon(action.icon) : null; return { id: action?.id || action?.label || `action-${index}`, label: action?.label, icon: action?.icon, disabled: action?.disabled, theme: action?.theme, chromeless: action?.chromeless, onPress: action?.onPress || fallbackHandler, iconComponent: IconComponent }; } export function FormView({ open = false, onClose = null, title = 'Edit Record', toolbar = [], fields = [], values = {}, onChange = () => {}, onSubmit = () => {}, onReset = () => {}, buttons = [], loading = false, errors = {}, children = null, hideButtons = false, width = 460 }) { const processedFields = useMemo(() => { return fields.map((field) => { if (!field.expression) { return field; } const expressionFunction = typeof field.expression === 'string' ? (form) => defaultExpressionEvaluator(field.expression, form) : field.expression; return { ...field, expressionFunction, readOnly: true }; }); }, [fields]); const computedValues = useMemo(() => { return processedFields.reduce((accumulator, field) => { if (field.expressionFunction) { accumulator[field.id] = field.expressionFunction(values); } return accumulator; }, {}); }, [processedFields, values]); const mergedValues = { ...values, ...computedValues }; const footerActions = useMemo(() => { if (hideButtons) { return []; } const sourceButtons = buttons.length > 0 ? buttons : [ { label: 'Reset', chromeless: true, onPress: onReset }, { label: 'Save', theme: 'active', onPress: onSubmit } ]; return sourceButtons.map((button, index) => renderAction(button, index, index === 0 ? onReset : onSubmit)); }, [buttons, hideButtons, onReset, onSubmit]); const renderField = (field, index) => ( ); const renderChildren = () => { if (!children) { return null; } return React.Children.map(children, (child) => { if (React.isValidElement(child) && child.type === FormField) { const fieldId = child.props.id; return React.cloneElement(child, { value: mergedValues[fieldId], onChange, error: errors[fieldId], disabled: child.props.disabled || loading }); } return child; }); }; return ( {processedFields.map(renderField)} {children ? ( {renderChildren()} ) : null} {loading ? ( ) : null} ); } export default FormView;