Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
143 lines
4.1 KiB
TypeScript
143 lines
4.1 KiB
TypeScript
'use client'
|
|
|
|
import type { ComponentProps, ReactNode } from 'react'
|
|
|
|
import {
|
|
Description,
|
|
FieldError,
|
|
Label,
|
|
Checkbox as HeroCheckbox,
|
|
Radio as HeroRadio,
|
|
RadioGroup as HeroRadioGroup,
|
|
Switch as HeroSwitch,
|
|
} from '@heroui/react'
|
|
|
|
import { cn } from '@/lib/cn'
|
|
|
|
interface ControlClassNames {
|
|
base?: string
|
|
label?: string
|
|
wrapper?: string
|
|
}
|
|
|
|
type CheckboxProps = Omit<ComponentProps<typeof HeroCheckbox>, 'children' | 'className' | 'onChange' | 'size'> & {
|
|
children?: ReactNode
|
|
className?: string
|
|
classNames?: ControlClassNames
|
|
color?: string
|
|
isInvalid?: boolean
|
|
size?: 'sm' | 'md' | 'lg'
|
|
onValueChange?: (selected: boolean) => void
|
|
}
|
|
|
|
export function Checkbox({ children, className, classNames, color: _color, isInvalid, size, onValueChange, ...props }: CheckboxProps) {
|
|
return (
|
|
<HeroCheckbox
|
|
{...props}
|
|
className={cn(classNames?.base, className)}
|
|
isInvalid={isInvalid}
|
|
onChange={onValueChange}
|
|
>
|
|
<HeroCheckbox.Content className={cn('flex items-center gap-2', size === 'lg' && 'text-lg')}>
|
|
<HeroCheckbox.Control className={classNames?.wrapper}>
|
|
<HeroCheckbox.Indicator />
|
|
</HeroCheckbox.Control>
|
|
<span className={classNames?.label}>{children}</span>
|
|
</HeroCheckbox.Content>
|
|
</HeroCheckbox>
|
|
)
|
|
}
|
|
|
|
type SwitchProps = Omit<ComponentProps<typeof HeroSwitch>, 'children' | 'className' | 'onChange'> & {
|
|
children?: ReactNode
|
|
className?: string
|
|
classNames?: ControlClassNames
|
|
size?: 'sm' | 'md' | 'lg'
|
|
onValueChange?: (selected: boolean) => void
|
|
}
|
|
|
|
export function Switch({ children, className, classNames, size, onValueChange, ...props }: SwitchProps) {
|
|
return (
|
|
<HeroSwitch
|
|
{...props}
|
|
className={cn(classNames?.base, className)}
|
|
size={size}
|
|
onChange={onValueChange}
|
|
>
|
|
<HeroSwitch.Content className="flex items-center gap-2">
|
|
<HeroSwitch.Control className={classNames?.wrapper}>
|
|
<HeroSwitch.Thumb />
|
|
</HeroSwitch.Control>
|
|
<span className={classNames?.label}>{children}</span>
|
|
</HeroSwitch.Content>
|
|
</HeroSwitch>
|
|
)
|
|
}
|
|
|
|
type RadioGroupProps = Omit<ComponentProps<typeof HeroRadioGroup>, 'children' | 'className' | 'onChange' | 'orientation'> & {
|
|
children?: ReactNode
|
|
className?: string
|
|
classNames?: ControlClassNames
|
|
description?: ReactNode
|
|
descriptionClassName?: string
|
|
errorMessage?: ReactNode
|
|
label?: ReactNode
|
|
orientation?: 'horizontal' | 'vertical'
|
|
size?: 'sm' | 'md' | 'lg'
|
|
onValueChange?: (value: string) => void
|
|
}
|
|
|
|
export function RadioGroup({
|
|
children,
|
|
className,
|
|
classNames,
|
|
description,
|
|
descriptionClassName,
|
|
errorMessage,
|
|
isInvalid,
|
|
label,
|
|
orientation = 'vertical',
|
|
size: _size,
|
|
onValueChange,
|
|
...props
|
|
}: RadioGroupProps) {
|
|
return (
|
|
<HeroRadioGroup
|
|
{...props}
|
|
// !flex-col: HeroUI روی orientation=horizontal کل گروه را ردیفی میکند و لیبل کنار گزینهها میافتد
|
|
className={cn('flex !flex-col gap-2', className)}
|
|
isInvalid={isInvalid}
|
|
orientation={orientation}
|
|
onChange={onValueChange}
|
|
>
|
|
{label ? <Label className={classNames?.label}>{label}</Label> : null}
|
|
<div className={cn('flex gap-4', orientation === 'horizontal' ? 'flex-row flex-wrap' : 'flex-col')}>{children}</div>
|
|
{description ? <Description className={descriptionClassName}>{description}</Description> : null}
|
|
{isInvalid && errorMessage ? <FieldError>{errorMessage}</FieldError> : null}
|
|
</HeroRadioGroup>
|
|
)
|
|
}
|
|
|
|
type RadioProps = Omit<ComponentProps<typeof HeroRadio>, 'children' | 'className'> & {
|
|
children?: ReactNode
|
|
className?: string
|
|
classNames?: ControlClassNames
|
|
size?: 'sm' | 'md' | 'lg'
|
|
}
|
|
|
|
export function Radio({ children, className, classNames, size: _size, ...props }: RadioProps) {
|
|
return (
|
|
<HeroRadio
|
|
{...props}
|
|
className={cn(classNames?.base, className)}
|
|
>
|
|
<HeroRadio.Content className="flex items-center gap-2">
|
|
<HeroRadio.Control className={classNames?.wrapper}>
|
|
<HeroRadio.Indicator />
|
|
</HeroRadio.Control>
|
|
<span className={classNames?.label}>{children}</span>
|
|
</HeroRadio.Content>
|
|
</HeroRadio>
|
|
)
|
|
}
|