import { type SVGProps } from 'react' import { Radio as RadioPrimitive } from '@base-ui/react/radio' import { RadioGroup as Radio } from '@base-ui/react/radio-group' import { CircleCheck, Palette, RotateCcw } from 'lucide-react' import { useTranslation } from 'react-i18next' import { IconDir } from '@/assets/custom/icon-dir' import { IconLayoutCompact } from '@/assets/custom/icon-layout-compact' import { IconLayoutDefault } from '@/assets/custom/icon-layout-default' import { IconLayoutFull } from '@/assets/custom/icon-layout-full' import { IconSidebarFloating } from '@/assets/custom/icon-sidebar-floating' import { IconSidebarInset } from '@/assets/custom/icon-sidebar-inset' import { IconSidebarSidebar } from '@/assets/custom/icon-sidebar-sidebar' import { IconThemeDark } from '@/assets/custom/icon-theme-dark' import { IconThemeLight } from '@/assets/custom/icon-theme-light' import { IconThemeSystem } from '@/assets/custom/icon-theme-system' import { type ContentLayout, THEME_PRESETS, type ThemePreset, type ThemeRadius, type ThemeScale, } from '@/lib/theme-customization' import { cn } from '@/lib/utils' import { useDirection } from '@/context/direction-provider' import { type Collapsible, useLayout } from '@/context/layout-provider' import { useThemeCustomization } from '@/context/theme-customization-provider' import { useTheme } from '@/context/theme-provider' import { Button } from '@/components/ui/button' import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, } from '@/components/ui/sheet' import { useSidebar } from './ui/sidebar' const Item = RadioPrimitive.Root export function ConfigDrawer() { const { t } = useTranslation() const { setOpen } = useSidebar() const { resetDir } = useDirection() const { resetTheme } = useTheme() const { resetLayout } = useLayout() const { resetCustomization } = useThemeCustomization() const handleReset = () => { setOpen(true) resetDir() resetTheme() resetLayout() resetCustomization() } return ( } > {t('Theme Settings')} {t('Adjust the appearance and layout to suit your preferences.')}
) } function SectionTitle(props: { title: string showReset?: boolean onReset?: () => void className?: string }) { return (
{props.title} {props.showReset && props.onReset && ( )}
) } function RadioGroupItem(props: { item: { value: string label: string icon: (props: SVGProps) => React.ReactElement } isTheme?: boolean }) { const isTheme = props.isTheme ?? false return (
{props.item.label}
) } function ThemeConfig() { const { t } = useTranslation() const { defaultTheme, theme, setTheme } = useTheme() return (
setTheme(defaultTheme)} /> {[ { value: 'system', label: t('System'), icon: IconThemeSystem }, { value: 'light', label: t('Light'), icon: IconThemeLight }, { value: 'dark', label: t('Dark'), icon: IconThemeDark }, ].map((item) => ( ))}
{t('Choose between system preference, light mode, or dark mode')}
) } function PresetConfig() { const { t } = useTranslation() const { defaults, customization, setPreset } = useThemeCustomization() return (
setPreset(defaults.preset)} /> setPreset(v as ThemePreset)} className='grid w-full grid-cols-4 gap-3' aria-label={t('Select color preset')} > {THEME_PRESETS.map((preset) => (
{t(`preset.${preset.value}`)}
))}
) } const RADIUS_OPTIONS: { value: ThemeRadius label: string // CSS border-radius value used to render the visual preview corner. preview: string }[] = [ { value: 'default', label: 'Auto', preview: '999px' }, { value: 'none', label: '0', preview: '0' }, { value: 'sm', label: '0.3', preview: '0.3rem' }, { value: 'md', label: '0.5', preview: '0.5rem' }, { value: 'lg', label: '0.75', preview: '0.75rem' }, { value: 'xl', label: '1.0', preview: '1rem' }, ] function RadiusConfig() { const { t } = useTranslation() const { defaults, customization, setRadius } = useThemeCustomization() return (
setRadius(defaults.radius)} /> setRadius(v as ThemeRadius)} className='grid w-full grid-cols-6 gap-2' aria-label={t('Select border radius')} > {RADIUS_OPTIONS.map((option) => (
{option.label}
))}
) } /** * Visual preview rows for the density preset. Each row's height represents * the relative line-height density (compact = tight rows, comfortable = wide). */ function ScalePreview(props: { rows: number; rowGap: string }) { return ( ) } function ScaleConfig() { const { t } = useTranslation() const { defaults, customization, setScale } = useThemeCustomization() const scaleOptions: { value: ThemeScale label: string rows: number rowGap: string }[] = [ { value: 'sm', label: t('Compact'), rows: 4, rowGap: '3px' }, { value: 'default', label: t('Default'), rows: 3, rowGap: '6px' }, { value: 'lg', label: t('Comfortable'), rows: 2, rowGap: '10px' }, ] return (
setScale(defaults.scale)} /> setScale(v as ThemeScale)} className='grid w-full grid-cols-3 gap-4' aria-label={t('Select interface density')} > {scaleOptions.map((option) => (
{option.label}
))}
) } function SidebarConfig() { const { t } = useTranslation() const { defaultVariant, variant, setVariant } = useLayout() return (
setVariant(defaultVariant)} /> {[ { value: 'inset', label: t('Inset'), icon: IconSidebarInset }, { value: 'floating', label: t('Floating'), icon: IconSidebarFloating, }, { value: 'sidebar', label: t('Sidebar'), icon: IconSidebarSidebar }, ].map((item) => ( ))}
) } function LayoutConfig() { const { t } = useTranslation() const { open, setOpen } = useSidebar() const { defaultCollapsible, collapsible, setCollapsible } = useLayout() const radioState = open ? 'default' : collapsible return (
{ setOpen(true) setCollapsible(defaultCollapsible) }} /> { if (v === 'default') { setOpen(true) return } setOpen(false) setCollapsible(v as Collapsible) }} className='grid w-full max-w-md grid-cols-3 gap-4' aria-label={t('Select layout style')} aria-describedby='layout-description' > {[ { value: 'default', label: t('Default'), icon: IconLayoutDefault }, { value: 'icon', label: t('Compact'), icon: IconLayoutCompact }, { value: 'offcanvas', label: t('Full layout'), icon: IconLayoutFull, }, ].map((item) => ( ))}
{t( 'Choose between default expanded, compact icon-only, or full layout mode' )}
) } function ContentLayoutConfig() { const { t } = useTranslation() const { defaults, customization, setContentLayout } = useThemeCustomization() return (
setContentLayout(defaults.contentLayout)} /> setContentLayout(v as ContentLayout)} className='grid w-full grid-cols-2 gap-4' aria-label={t('Select content width')} > {[ { value: 'full', label: t('Full width') }, { value: 'centered', label: t('Centered') }, ].map((option) => (
{option.label}
))}
) } /** * Mini "page" mock used as the visual preview for content-width options. * `full` fills horizontally, `centered` clamps the body to a narrow column. */ function ContentLayoutPreview(props: { centered: boolean }) { return ( ) } function DirConfig() { const { t } = useTranslation() const { defaultDir, dir, setDir } = useDirection() return (
setDir(defaultDir)} /> {[ { value: 'ltr', label: t('Left to Right'), icon: (props: SVGProps) => ( ), }, { value: 'rtl', label: t('Right to Left'), icon: (props: SVGProps) => ( ), }, ].map((item) => ( ))}
{t('Choose between left-to-right or right-to-left site direction')}
) }