import { cva } from 'class-variance-authority'; import * as React from 'react'; import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling'; /* ------------------------------------------------------------------------------------------------- * Anatomy * -----------------------------------------------------------------------------------------------*/ export const BasicFieldAnatomy = defineStyleAnatomy({ fieldLabel: cva([ 'UI-BasicField__fieldLabel', 'text-base w-fit font-semibold self-start', 'data-[error=true]:text-red-500', ]), fieldAsterisk: cva('UI-BasicField__fieldAsterisk ml-1 text-red-500 text-sm'), fieldDetails: cva('UI-BasicField__fieldDetails'), field: cva('UI-BasicField__field relative w-full space-y-1'), fieldHelpText: cva('UI-BasicField__fieldHelpText text-sm text-[--muted]'), fieldErrorText: cva('UI-BasicField__fieldErrorText text-sm text-red-500'), }); /* ------------------------------------------------------------------------------------------------- * BasicFieldOptions * - Field components inherit these props * -----------------------------------------------------------------------------------------------*/ export type BasicFieldOptions = ComponentAnatomy & { /** * The id of the field. If not provided, a unique id will be generated. */ id?: string | undefined; /** * The form field name. */ name?: string; /** * The label of the field. */ label?: React.ReactNode; /** * Additional props to pass to the label element. */ labelProps?: React.LabelHTMLAttributes; /** * Help or description text to display below the field. */ help?: React.ReactNode; /** * Error text to display below the field. */ error?: string; /** * If `true`, the field will be required. */ required?: boolean; /** * If `true`, the field will be disabled. */ disabled?: boolean; /** * If `true`, the field will be readonly. */ readonly?: boolean; }; /* ------------------------------------------------------------------------------------------------- * Extract BasicFieldProps * -----------------------------------------------------------------------------------------------*/ export function extractBasicFieldProps( props: Props, id: string ) { const { name, label, labelProps, help, error, required, disabled = false, readonly = false, fieldDetailsClass, fieldLabelClass, fieldAsteriskClass, fieldClass, fieldErrorTextClass, fieldHelpTextClass, id: _id, ...rest } = props; return [ rest, { id: _id || id, name, label, help, error, disabled, required, readonly, fieldAsteriskClass, fieldErrorTextClass, fieldHelpTextClass, fieldDetailsClass, fieldLabelClass, fieldClass, labelProps, }, ] as [ Omit< Props, | 'label' | 'name' | 'help' | 'error' | 'disabled' | 'required' | 'readonly' | 'fieldDetailsClass' | 'fieldLabelClass' | 'fieldClass' | 'fieldHelpTextClass' | 'fieldErrorTextClass' | 'id' | 'labelProps' | 'fieldAsteriskClass' >, Omit & { id: string; }, ]; } /* ------------------------------------------------------------------------------------------------- * BasicField * -----------------------------------------------------------------------------------------------*/ export type BasicFieldProps = React.ComponentPropsWithoutRef<'div'> & BasicFieldOptions; export const BasicField = React.memo( React.forwardRef((props, ref) => { const { children, className, labelProps, id, label, error, help, disabled, readonly, required, fieldClass, fieldDetailsClass, fieldLabelClass, fieldAsteriskClass, fieldErrorTextClass, fieldHelpTextClass, ...rest } = props; return (
{!!label && ( )} {children} {(!!help || !!error) && (
{!!help && (
{help}
)} {!!error && (
{error}
)}
)}
); }) ); BasicField.displayName = 'BasicField';