144 lines
3.8 KiB
JavaScript
144 lines
3.8 KiB
JavaScript
import React, { createElement, forwardRef, useContext, useId } from 'react'
|
|
import { ExclamationCircleIcon } from '@heroicons/react/24/outline'
|
|
import { omit } from 'ramda'
|
|
import { Loader } from './loader'
|
|
import clsx from 'clsx'
|
|
|
|
const ControlContext = React.createContext({})
|
|
|
|
function ControlProvider({ children, ...props }) {
|
|
return (
|
|
<ControlContext.Provider value={props}>{children}</ControlContext.Provider>
|
|
)
|
|
}
|
|
|
|
function useControl(props) {
|
|
const field = useContext(ControlContext)
|
|
return { ...field, ...props }
|
|
}
|
|
|
|
export const Control = forwardRef(function Control(
|
|
{ as = 'div', children, ...props },
|
|
ref,
|
|
) {
|
|
const id = useId()
|
|
const props_ = omit(['id'], props)
|
|
|
|
return (
|
|
<ControlProvider id={props?.id || id} {...props_}>
|
|
{React.createElement(as, { ref, ...props }, children)}
|
|
</ControlProvider>
|
|
)
|
|
})
|
|
|
|
export function Button({
|
|
children,
|
|
as = 'button',
|
|
className,
|
|
isLoading = false,
|
|
...props
|
|
}) {
|
|
if (isLoading) {
|
|
props['disabled'] = isLoading
|
|
}
|
|
|
|
return createElement(
|
|
as,
|
|
{
|
|
className: clsx(
|
|
'font-medium text-green-primary rounded-lg bg-green-secondary hover:bg-green-support',
|
|
'h-12 px-4 relative',
|
|
'disabled:bg-green-secondary/50 disabled:pointer-events-none',
|
|
'transition',
|
|
className,
|
|
),
|
|
...props,
|
|
},
|
|
<>
|
|
{isLoading && (
|
|
<div className="absolute inset-0 flex items-center justify-center bg-green-secondary rounded-xl">
|
|
<Loader className="w-5 text-white" />
|
|
</div>
|
|
)}
|
|
|
|
{children}
|
|
</>,
|
|
)
|
|
}
|
|
|
|
export function Label({ children, className, ...props }) {
|
|
const { id, htmlFor, className: _, ...field } = useControl(props)
|
|
|
|
return (
|
|
<label
|
|
className={clsx(
|
|
"black cursor-pointer aria-required:after:content-['*'] aria-required:after:ml-0.5 aria-required:after:text-red-500",
|
|
className,
|
|
)}
|
|
htmlFor={htmlFor ?? id}
|
|
{...field}
|
|
>
|
|
{children}
|
|
</label>
|
|
)
|
|
}
|
|
|
|
export const Input = forwardRef(function Input(
|
|
{ as = 'input', size = 'base', className, children, ...props },
|
|
ref,
|
|
) {
|
|
const { className: _, ...field } = useControl(props)
|
|
const sizes = { base: 'h-12' }
|
|
|
|
return createElement(
|
|
as,
|
|
{
|
|
className: clsx(
|
|
'bg-white outline-none px-4 rounded-lg transition',
|
|
'border border-green-light dark:border-gray-700 dark:bg-gray-800',
|
|
'focus:ring-1 focus:border-green-secondary focus:ring-green-secondary focus:placeholder:text-transparent',
|
|
'aria-[invalid=true]:border-red-400 aria-[invalid=true]:ring-red-400',
|
|
'dark:aria-[invalid=true]:border-red-500 dark:aria-[invalid=true]:ring-red-500',
|
|
'disabled:text-gray-400 disabled:border-gray-300 disabled:bg-gray-200',
|
|
'dark:disabled:text-gray-500 dark:disabled:bg-gray-700',
|
|
sizes?.[size],
|
|
className,
|
|
),
|
|
ref,
|
|
...field,
|
|
},
|
|
children,
|
|
)
|
|
})
|
|
|
|
export const Checkbox = forwardRef(function Checkbox(
|
|
{ className, ...props },
|
|
ref,
|
|
) {
|
|
const { className: _, ...field } = useControl(props)
|
|
|
|
return (
|
|
<input
|
|
type="checkbox"
|
|
className={clsx(
|
|
// 'text-green-secondary border border-gray-300',
|
|
// 'focus:ring-2 focus:border-green-secondary focus:ring-green-secondary focus:ring-offset-0 focus:ring-opacity-30',
|
|
// 'dark:border-gray-700 dark:bg-gray-800 focus:dark:border-security dark:checked:bg-green-secondary dark:checked:border-security dark:disabled:bg-gray-700',
|
|
// 'disabled:bg-gray-200 outline-none rounded transition',
|
|
className,
|
|
)}
|
|
ref={ref}
|
|
{...field}
|
|
/>
|
|
)
|
|
})
|
|
|
|
export function Error({ children }) {
|
|
return (
|
|
<div className="text-sm text-red-500 flex items-start gap-0.5">
|
|
<ExclamationCircleIcon className="w-4 mt-[1.5px] flex-shrink-0" />
|
|
<div>{children}</div>
|
|
</div>
|
|
)
|
|
}
|