Spaces:
Build error
Build error
import { cva, VariantProps } from 'class-variance-authority'; | |
import * as React from 'react'; | |
import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling'; | |
/* ------------------------------------------------------------------------------------------------- | |
* Anatomy | |
* -----------------------------------------------------------------------------------------------*/ | |
export const InputAnatomy = defineStyleAnatomy({ | |
root: cva( | |
[ | |
'UI-Input__root', | |
'flex items-center', | |
'w-full rounded-[--radius]', | |
'bg-[--paper] border border-[--border] placeholder-gray-400 dark:placeholder-gray-500', | |
'disabled:cursor-not-allowed', | |
'data-[disable=true]:shadow-none data-[disable=true]:opacity-50', | |
'focus:border-brand focus:ring-1 focus:ring-[--ring]', | |
'outline-0', | |
'transition duration-150', | |
'shadow-sm', | |
], | |
{ | |
variants: { | |
size: { | |
sm: 'h-8 px-2 py-1 text-sm', | |
md: 'h-10 px-3', | |
lg: 'h-12 px-4 py-3 text-md', | |
}, | |
intent: { | |
basic: 'hover:border-gray-300 dark:hover:border-gray-600', | |
filled: | |
'bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 border-transparent focus:bg-white dark:focus:bg-gray-900 shadow-none', | |
unstyled: | |
'bg-transparent hover:bg-transparent border-0 shadow-none focus:ring-0 rounded-none p-0 text-base', | |
}, | |
hasError: { | |
false: null, | |
true: 'border-red-500 hover:border-red-200 dark:border-red-500', | |
}, | |
isDisabled: { | |
false: null, | |
true: 'shadow-none pointer-events-none opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800', | |
}, | |
isReadonly: { | |
false: null, | |
true: 'pointer-events-none cursor-not-allowed shadow-sm', | |
}, | |
hasLeftAddon: { true: null, false: null }, | |
hasRightAddon: { true: null, false: null }, | |
hasLeftIcon: { true: null, false: null }, | |
hasRightIcon: { true: null, false: null }, | |
}, | |
compoundVariants: [ | |
{ | |
hasLeftAddon: true, | |
className: | |
'border-l-transparent hover:border-l-transparent rounded-l-none', | |
}, | |
/**/ | |
{ | |
hasRightAddon: true, | |
className: | |
'border-r-transparent hover:border-r-transparent rounded-r-none', | |
}, | |
/**/ | |
{ | |
hasLeftAddon: false, | |
hasLeftIcon: true, | |
size: 'sm', | |
className: 'pl-10', | |
}, | |
{ | |
hasLeftAddon: false, | |
hasLeftIcon: true, | |
size: 'md', | |
className: 'pl-10', | |
}, | |
{ | |
hasLeftAddon: false, | |
hasLeftIcon: true, | |
size: 'lg', | |
className: 'pl-12', | |
}, | |
/**/ | |
{ | |
hasRightAddon: false, | |
hasRightIcon: true, | |
size: 'sm', | |
className: 'pr-10', | |
}, | |
{ | |
hasRightAddon: false, | |
hasRightIcon: true, | |
size: 'md', | |
className: 'pr-10', | |
}, | |
{ | |
hasRightAddon: false, | |
hasRightIcon: true, | |
size: 'lg', | |
className: 'pr-12', | |
}, | |
], | |
defaultVariants: { | |
size: 'md', | |
intent: 'basic', | |
hasError: false, | |
isDisabled: false, | |
hasLeftIcon: false, | |
hasRightIcon: false, | |
hasLeftAddon: false, | |
hasRightAddon: false, | |
}, | |
} | |
), | |
}); | |
export const hiddenInputStyles = cn( | |
'appearance-none absolute bottom-0 border-0 w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap [clip:rect(0px,0px,0px,0px)] [overflow-wrap:normal]' | |
); | |
/* ------------------------------------------------------------------------------------------------- | |
* InputContainer | |
* -----------------------------------------------------------------------------------------------*/ | |
export const InputContainerAnatomy = defineStyleAnatomy({ | |
inputContainer: cva(['UI-Input__inputContainer', 'flex relative']), | |
}); | |
export type InputContainerProps = { | |
className: React.HTMLAttributes<HTMLDivElement>['className']; | |
children?: React.ReactNode; | |
}; | |
export const InputContainer = ({ | |
className, | |
children, | |
}: InputContainerProps) => { | |
return ( | |
<div className={cn('UI-Input__inputContainer flex relative', className)}> | |
{children} | |
</div> | |
); | |
}; | |
/* ------------------------------------------------------------------------------------------------- | |
* InputStyling | |
* -----------------------------------------------------------------------------------------------*/ | |
export type InputStyling = Omit< | |
VariantProps<typeof InputAnatomy.root>, | |
| 'isDisabled' | |
| 'hasError' | |
| 'hasLeftAddon' | |
| 'hasRightAddon' | |
| 'hasLeftIcon' | |
| 'hasRightIcon' | |
> & | |
ComponentAnatomy<typeof InputAddonsAnatomy> & | |
ComponentAnatomy<typeof InputContainerAnatomy> & { | |
leftAddon?: React.ReactNode; | |
leftIcon?: React.ReactNode; | |
rightAddon?: React.ReactNode; | |
rightIcon?: React.ReactNode; | |
}; | |
/* ------------------------------------------------------------------------------------------------- | |
* Addons Anatomy | |
* -----------------------------------------------------------------------------------------------*/ | |
export const InputAddonsAnatomy = defineStyleAnatomy({ | |
icon: cva( | |
[ | |
'UI-Input__addons--icon', | |
'pointer-events-none absolute inset-y-0 grid place-content-center text-gray-500', | |
'dark:text-gray-300 !z-[1]', | |
], | |
{ | |
variants: { | |
size: { sm: 'w-10 text-md', md: 'w-12 text-lg', lg: 'w-14 text-2xl' }, | |
isLeftIcon: { true: 'left-0', false: null }, | |
isRightIcon: { true: 'right-0', false: null }, | |
}, | |
defaultVariants: { | |
size: 'md', | |
isLeftIcon: false, | |
isRightIcon: false, | |
}, | |
} | |
), | |
addon: cva( | |
[ | |
'UI-Input__addons--addon', | |
'bg-gray-50 inline-flex items-center flex-none px-3 border border-gray-300 text-gray-800 shadow-sm text-sm sm:text-md', | |
'dark:bg-[--paper] dark:border-[--border] dark:text-gray-300', | |
], | |
{ | |
variants: { | |
size: { sm: 'text-sm', md: 'text-md', lg: 'text-lg' }, | |
isLeftAddon: { true: 'rounded-l-md border-r-0', false: null }, | |
isRightAddon: { true: 'rounded-r-md border-l-0', false: null }, | |
hasLeftIcon: { true: null, false: null }, | |
hasRightIcon: { true: null, false: null }, | |
}, | |
compoundVariants: [ | |
{ | |
size: 'sm', | |
hasLeftIcon: true, | |
isLeftAddon: true, | |
className: 'pl-10', | |
}, | |
{ | |
size: 'sm', | |
hasRightIcon: true, | |
isRightAddon: true, | |
className: 'pr-10', | |
}, | |
{ | |
size: 'md', | |
hasLeftIcon: true, | |
isLeftAddon: true, | |
className: 'pl-10', | |
}, | |
{ | |
size: 'md', | |
hasRightIcon: true, | |
isRightAddon: true, | |
className: 'pr-10', | |
}, | |
{ | |
size: 'lg', | |
hasLeftIcon: true, | |
isLeftAddon: true, | |
className: 'pl-12', | |
}, | |
{ | |
size: 'lg', | |
hasRightIcon: true, | |
isRightAddon: true, | |
className: 'pr-12', | |
}, | |
], | |
defaultVariants: { | |
size: 'md', | |
isLeftAddon: false, | |
isRightAddon: false, | |
hasLeftIcon: false, | |
hasRightIcon: false, | |
}, | |
} | |
), | |
}); | |
/* ------------------------------------------------------------------------------------------------- | |
* InputIcon | |
* -----------------------------------------------------------------------------------------------*/ | |
export type InputIconProps = { | |
icon: InputStyling['leftIcon'] | undefined; | |
size: InputStyling['size']; | |
side: 'right' | 'left'; | |
props?: Omit<React.ComponentPropsWithoutRef<'span'>, 'className'>; | |
className?: string; | |
}; | |
export const InputIcon = ({ | |
icon, | |
size = 'md', | |
side, | |
props, | |
className, | |
}: InputIconProps) => { | |
if (!!icon) | |
return ( | |
<span | |
className={cn( | |
InputAddonsAnatomy.icon({ | |
isRightIcon: side === 'right', | |
isLeftIcon: side === 'left', | |
size, | |
}), | |
className | |
)} | |
{...props} | |
> | |
{icon} | |
</span> | |
); | |
return null; | |
}; | |
/* ------------------------------------------------------------------------------------------------- | |
* InputAddon | |
* -----------------------------------------------------------------------------------------------*/ | |
export type InputAddonProps = { | |
addon: InputStyling['rightAddon'] | InputStyling['leftAddon'] | undefined; | |
rightIcon: InputStyling['leftIcon'] | undefined; | |
leftIcon: InputStyling['rightIcon'] | undefined; | |
size: InputStyling['size']; | |
side: 'right' | 'left'; | |
props?: Omit<React.ComponentPropsWithoutRef<'span'>, 'className'>; | |
className?: string; | |
}; | |
export const InputAddon = ({ | |
addon, | |
leftIcon, | |
rightIcon, | |
size = 'md', | |
side, | |
props, | |
className, | |
}: InputAddonProps) => { | |
if (!!addon) | |
return ( | |
<span | |
className={cn( | |
InputAddonsAnatomy.addon({ | |
isRightAddon: side === 'right', | |
isLeftAddon: side === 'left', | |
hasRightIcon: !!rightIcon, | |
hasLeftIcon: !!leftIcon, | |
size, | |
}), | |
className | |
)} | |
{...props} | |
> | |
{addon} | |
</span> | |
); | |
return null; | |
}; | |
/* ------------------------------------------------------------------------------------------------- | |
* Utils | |
* -----------------------------------------------------------------------------------------------*/ | |
export function extractInputPartProps<T extends InputStyling>(props: T) { | |
const { | |
size, | |
leftAddon, | |
leftIcon, | |
rightAddon, | |
rightIcon, | |
inputContainerClass, // class | |
iconClass, // class | |
addonClass, // class | |
...rest | |
} = props; | |
return [ | |
{ | |
size, | |
leftAddon, | |
leftIcon, | |
rightAddon, | |
rightIcon, | |
...rest, | |
}, | |
{ | |
inputContainerProps: { | |
className: inputContainerClass, | |
}, | |
leftAddonProps: { | |
addon: leftAddon, | |
leftIcon, | |
rightIcon, | |
size, | |
side: 'left', | |
className: addonClass, | |
}, | |
rightAddonProps: { | |
addon: rightAddon, | |
leftIcon, | |
rightIcon, | |
size, | |
side: 'right', | |
className: addonClass, | |
}, | |
leftIconProps: { | |
icon: leftIcon, | |
size, | |
side: 'left', | |
className: iconClass, | |
}, | |
rightIconProps: { | |
icon: rightIcon, | |
size, | |
side: 'right', | |
className: iconClass, | |
}, | |
}, | |
] as [ | |
Omit<T, 'iconClass' | 'addonClass' | 'inputContainerClass'>, | |
{ | |
inputContainerProps: InputContainerProps; | |
leftAddonProps: InputAddonProps; | |
rightAddonProps: InputAddonProps; | |
leftIconProps: InputIconProps; | |
rightIconProps: InputIconProps; | |
}, | |
]; | |
} | |