107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
import type { ChangeEvent } from 'react';
|
|
import type { FC, TeactNode } from '../../lib/teact/teact';
|
|
import React, { memo, useState } from '../../lib/teact/teact';
|
|
|
|
import type { ApiUser } from '../../api/types';
|
|
|
|
import useLastCallback from '../../hooks/useLastCallback';
|
|
|
|
import Checkbox from './Checkbox';
|
|
|
|
export type IRadioOption = {
|
|
label: TeactNode;
|
|
subLabel?: string;
|
|
disabled?: boolean;
|
|
value: string;
|
|
nestedOptions?: IRadioOption[];
|
|
user?: ApiUser;
|
|
};
|
|
|
|
type OwnProps = {
|
|
id?: string;
|
|
options: IRadioOption[];
|
|
selected?: string[];
|
|
disabled?: boolean;
|
|
nestedCheckbox?: boolean;
|
|
loadingOptions?: string[];
|
|
isRound?: boolean;
|
|
onChange: (value: string[]) => void;
|
|
};
|
|
|
|
const CheckboxGroup: FC<OwnProps> = ({
|
|
id,
|
|
options,
|
|
selected = [],
|
|
disabled,
|
|
nestedCheckbox,
|
|
loadingOptions,
|
|
isRound,
|
|
onChange,
|
|
}) => {
|
|
const [values, setValues] = useState<string[]>(selected || []);
|
|
|
|
const handleChange = useLastCallback((event: ChangeEvent<HTMLInputElement>, nestedOptionList?: IRadioOption) => {
|
|
const { value, checked } = event.currentTarget;
|
|
let newValues: string[];
|
|
|
|
if (checked) {
|
|
newValues = [...values, value];
|
|
if (nestedOptionList && value) {
|
|
newValues.push(nestedOptionList.value);
|
|
}
|
|
if (nestedOptionList && value === nestedOptionList.value) {
|
|
nestedOptionList.nestedOptions?.forEach((nestedOption) => {
|
|
if (!newValues.includes(nestedOption.value)) {
|
|
newValues.push(nestedOption.value);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
newValues = values.filter((v) => v !== value);
|
|
if (nestedOptionList && value === nestedOptionList.value) {
|
|
nestedOptionList.nestedOptions?.forEach((nestedOption) => {
|
|
newValues = newValues.filter((v) => v !== nestedOption.value);
|
|
});
|
|
} else if (nestedOptionList) {
|
|
const nestedOptionValues = nestedOptionList.nestedOptions?.map((nestedOption) => nestedOption.value) || [];
|
|
const hasOtherNestedValuesChecked = nestedOptionValues.some((nestedValue) => newValues.includes(nestedValue));
|
|
if (!hasOtherNestedValuesChecked) {
|
|
newValues = newValues.filter((v) => v !== nestedOptionList.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
setValues(newValues);
|
|
onChange(newValues);
|
|
});
|
|
const getCheckedNestedCount = useLastCallback((nestedOptions: IRadioOption[]) => {
|
|
const checkedCount = nestedOptions?.filter((nestedOption) => values.includes(nestedOption.value)).length;
|
|
return checkedCount > 0 ? checkedCount : undefined;
|
|
});
|
|
|
|
return (
|
|
<div id={id} className="radio-group">
|
|
{options.map((option) => {
|
|
return (
|
|
<Checkbox
|
|
label={option.label}
|
|
subLabel={option.subLabel}
|
|
value={option.value}
|
|
checked={selected.indexOf(option.value) !== -1}
|
|
disabled={option.disabled || disabled}
|
|
isLoading={loadingOptions ? loadingOptions.indexOf(option.value) !== -1 : undefined}
|
|
onChange={handleChange}
|
|
nestedCheckbox={nestedCheckbox}
|
|
nestedCheckboxCount={getCheckedNestedCount(option.nestedOptions ?? [])}
|
|
nestedOptionList={option}
|
|
values={values}
|
|
isRound={isRound}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default memo(CheckboxGroup);
|