feat(FE): Add checkbox multi-select and components prop

This commit is contained in:
rstubryan
2026-01-14 10:04:51 +07:00
parent e6eac6b62d
commit adb8d0f69e
3 changed files with 102 additions and 7 deletions
+18 -5
View File
@@ -35,6 +35,7 @@ interface SelectInputBaseProps<T = OptionType> {
bottomLabel?: ReactNode;
options: T[];
optionComponent?: OptionComponent<T>;
components?: Partial<typeof ReactSelectComponents>;
isDisabled?: boolean;
isLoading?: boolean;
isClearable?: boolean;
@@ -56,9 +57,12 @@ interface SelectInputBaseProps<T = OptionType> {
onInputChange?: (search: string) => void;
startAdornment?: ReactNode;
menuPortalTarget?: HTMLElement | null;
closeMenuOnSelect?: boolean;
hideSelectedOptions?: boolean;
}
interface SelectInputProps<T = OptionType> extends SelectInputBaseProps<T> {
export interface SelectInputProps<T = OptionType>
extends SelectInputBaseProps<T> {
createables?: boolean;
value?: T | T[] | null;
onChange?: (val: T | T[] | null) => void;
@@ -101,6 +105,7 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
onChange,
options,
optionComponent,
components: customComponents,
isDisabled,
isLoading,
isClearable,
@@ -119,6 +124,8 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
onInputChange,
startAdornment,
menuPortalTarget,
closeMenuOnSelect,
hideSelectedOptions,
} = props;
const [internalInputValue, setInternalInputValue] = useState('');
@@ -128,14 +135,18 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
const components = useMemo(() => {
const base = isAnimated ? animatedComponents : {};
const customComponents = { ...base, IndicatorSeparator: () => null };
const mergedComponents = { ...base, IndicatorSeparator: () => null };
if (startAdornment) {
customComponents.Control = CustomControl;
mergedComponents.Control = CustomControl;
}
return customComponents;
}, [isAnimated, startAdornment]);
if (customComponents) {
Object.assign(mergedComponents, customComponents);
}
return mergedComponents;
}, [isAnimated, startAdornment, customComponents]);
const internalInputChangeHandler = (val: string, meta: InputActionMeta) => {
if (meta.action === 'input-change') setInternalInputValue(val);
@@ -205,6 +216,8 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
isRtl={isRtl}
isSearchable={isSearchable}
placeholder={placeholder}
closeMenuOnSelect={closeMenuOnSelect}
hideSelectedOptions={hideSelectedOptions}
className={cn('w-full', className?.select)}
classNames={{
...(!startAdornment && {
@@ -0,0 +1,82 @@
'use client';
import { useMemo } from 'react';
import {
OptionProps,
GroupBase,
components as ReactSelectComponents,
} from 'react-select';
import SelectInput, { OptionType, SelectInputProps } from './SelectInput';
import { cn } from '@/lib/helper';
interface SelectInputCheckboxProps<T = OptionType>
extends Omit<
SelectInputProps<T>,
'closeMenuOnSelect' | 'hideSelectedOptions' | 'optionComponent'
> {
closeMenuOnSelect?: boolean;
hideSelectedOptions?: boolean;
}
const CheckboxOption = <
T extends OptionType,
IsMulti extends boolean,
Group extends GroupBase<T>,
>(
props: OptionProps<T, IsMulti, Group>
) => {
const { isSelected, label, innerRef, innerProps, className } = props;
return (
<div
ref={innerRef}
{...innerProps}
className={cn(
'flex items-center gap-2 px-3 py-2 cursor-pointer',
className
)}
>
<input
type='checkbox'
checked={isSelected}
onChange={() => null}
className='checkbox checkbox-sm checkbox-primary pointer-events-none'
/>
<label className='cursor-pointer flex-1 select-none'>{label}</label>
</div>
);
};
const SelectInputCheckbox = <T extends OptionType>(
props: SelectInputCheckboxProps<T>
) => {
const {
closeMenuOnSelect = false,
hideSelectedOptions = false,
isMulti = true,
className,
...restProps
} = props;
const customComponents = useMemo(() => {
return {
Option: CheckboxOption as typeof ReactSelectComponents.Option,
};
}, []);
return (
<SelectInput<T>
{...restProps}
isMulti={isMulti}
closeMenuOnSelect={closeMenuOnSelect}
hideSelectedOptions={hideSelectedOptions}
className={{
...className,
select: cn(className?.select, 'select-checkbox'),
}}
components={customComponents}
/>
);
};
export default SelectInputCheckbox;
@@ -7,6 +7,7 @@ import SelectInput, {
useSelect,
OptionType,
} from '@/components/input/SelectInput';
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
import DateInput from '@/components/input/DateInput';
import { CustomerApi } from '@/services/api/master-data';
import { FinanceApi } from '@/services/api/report/finance-report';
@@ -608,10 +609,9 @@ const CustomerPaymentTab = () => {
</div>
<div>
<SelectInput
<SelectInputCheckbox
label='Customer'
placeholder='Pilih Customer'
isMulti
options={customerOptions}
value={filterCustomer}
onChange={(val) => {