Spaces:
Build error
Build error
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<typeof BasicFieldAnatomy> & { | |
/** | |
* 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<HTMLLabelElement>; | |
/** | |
* 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 extends BasicFieldOptions>( | |
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<BasicFieldOptions, 'id'> & { | |
id: string; | |
}, | |
]; | |
} | |
/* ------------------------------------------------------------------------------------------------- | |
* BasicField | |
* -----------------------------------------------------------------------------------------------*/ | |
export type BasicFieldProps = React.ComponentPropsWithoutRef<'div'> & | |
BasicFieldOptions; | |
export const BasicField = React.memo( | |
React.forwardRef<HTMLDivElement, BasicFieldProps>((props, ref) => { | |
const { | |
children, | |
className, | |
labelProps, | |
id, | |
label, | |
error, | |
help, | |
disabled, | |
readonly, | |
required, | |
fieldClass, | |
fieldDetailsClass, | |
fieldLabelClass, | |
fieldAsteriskClass, | |
fieldErrorTextClass, | |
fieldHelpTextClass, | |
...rest | |
} = props; | |
return ( | |
<div | |
className={cn(BasicFieldAnatomy.field(), className, fieldClass)} | |
{...rest} | |
ref={ref} | |
> | |
{!!label && ( | |
<label | |
htmlFor={disabled ? undefined : id} | |
className={cn(BasicFieldAnatomy.fieldLabel(), fieldLabelClass)} | |
data-error={!!error} | |
{...labelProps} | |
> | |
{label} | |
{required && ( | |
<span | |
className={cn( | |
BasicFieldAnatomy.fieldAsterisk(), | |
fieldAsteriskClass | |
)} | |
> | |
* | |
</span> | |
)} | |
</label> | |
)} | |
{children} | |
{(!!help || !!error) && ( | |
<div | |
className={cn(BasicFieldAnatomy.fieldDetails(), fieldDetailsClass)} | |
> | |
{!!help && ( | |
<div | |
className={cn( | |
BasicFieldAnatomy.fieldHelpText(), | |
fieldHelpTextClass | |
)} | |
> | |
{help} | |
</div> | |
)} | |
{!!error && ( | |
<div | |
className={cn( | |
BasicFieldAnatomy.fieldErrorText(), | |
fieldErrorTextClass | |
)} | |
> | |
{error} | |
</div> | |
)} | |
</div> | |
)} | |
</div> | |
); | |
}) | |
); | |
BasicField.displayName = 'BasicField'; | |