'use client' import Badge from '@/app/components/base/badge' import Checkbox from '@/app/components/base/checkbox' import SearchInput from '@/app/components/base/search-input' import SearchMenu from '@/assets/search-menu.svg' import cn from '@/utils/classnames' import Image from 'next/image' import type { FC } from 'react' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '../button' export type CheckboxListOption = { label: string value: string disabled?: boolean } export type CheckboxListProps = { title?: string label?: string description?: string options: CheckboxListOption[] value?: string[] onChange?: (value: string[]) => void disabled?: boolean containerClassName?: string showSelectAll?: boolean showCount?: boolean showSearch?: boolean maxHeight?: string | number } const CheckboxList: FC = ({ title = '', label, description, options, value = [], onChange, disabled = false, containerClassName, showSelectAll = true, showCount = true, showSearch = true, maxHeight, }) => { const { t } = useTranslation() const [searchQuery, setSearchQuery] = useState('') const filteredOptions = useMemo(() => { if (!searchQuery?.trim()) return options const query = searchQuery.toLowerCase() return options.filter(option => option.label.toLowerCase().includes(query) || option.value.toLowerCase().includes(query), ) }, [options, searchQuery]) const selectedCount = value.length const isAllSelected = useMemo(() => { const selectableOptions = options.filter(option => !option.disabled) return selectableOptions.length > 0 && selectableOptions.every(option => value.includes(option.value)) }, [options, value]) const isIndeterminate = useMemo(() => { const selectableOptions = options.filter(option => !option.disabled) const selectedCount = selectableOptions.filter(option => value.includes(option.value)).length return selectedCount > 0 && selectedCount < selectableOptions.length }, [options, value]) const handleSelectAll = useCallback(() => { if (disabled) return if (isAllSelected) { // Deselect all onChange?.([]) } else { // Select all non-disabled options const allValues = options .filter(option => !option.disabled) .map(option => option.value) onChange?.(allValues) } }, [isAllSelected, options, onChange, disabled]) const handleToggleOption = useCallback((optionValue: string) => { if (disabled) return const newValue = value.includes(optionValue) ? value.filter(v => v !== optionValue) : [...value, optionValue] onChange?.(newValue) }, [value, onChange, disabled]) return (
{label && (
{label}
)} {description && (
{description}
)}
{(showSelectAll || title || showSearch) && (
{!searchQuery && showSelectAll && ( )} {!searchQuery ?
{title && ( {title} )} {showCount && selectedCount > 0 && ( {t('common.operation.selectCount', { count: selectedCount })} )}
:
{ filteredOptions.length > 0 ? t('common.operation.searchCount', { count: filteredOptions.length, content: title }) : t('common.operation.noSearchCount', { content: title })}
} {showSearch && ( )}
)}
{!filteredOptions.length ? (
{searchQuery ?
search menu {t('common.operation.noSearchResults', { content: title })}
: t('common.noData')}
) : ( filteredOptions.map((option) => { const selected = value.includes(option.value) return (
{ if (!option.disabled && !disabled) handleToggleOption(option.value) }} > { if (!option.disabled && !disabled) handleToggleOption(option.value) }} disabled={option.disabled || disabled} />
{option.label}
) }) )}
) } export default CheckboxList