Files
bknd/app/src/ui/components/form/json-schema/widgets/CheckboxesWidget.tsx
2024-11-16 12:01:47 +01:00

99 lines
3.4 KiB
TypeScript

import {
type FormContextType,
type RJSFSchema,
type StrictRJSFSchema,
type WidgetProps,
ariaDescribedByIds,
enumOptionsDeselectValue,
enumOptionsIsSelected,
enumOptionsSelectValue,
enumOptionsValueForIndex,
optionId
} from "@rjsf/utils";
import { type ChangeEvent, type FocusEvent, useCallback } from "react";
/** The `CheckboxesWidget` is a widget for rendering checkbox groups.
* It is typically used to represent an array of enums.
*
* @param props - The `WidgetProps` for this component
*/
function CheckboxesWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({
id,
disabled,
options: { inline = false, enumOptions, enumDisabled, emptyValue },
value,
autofocus = false,
readonly,
onChange,
onBlur,
onFocus
}: WidgetProps<T, S, F>) {
const checkboxesValues = Array.isArray(value) ? value : [value];
const handleBlur = useCallback(
({ target }: FocusEvent<HTMLInputElement>) =>
onBlur(id, enumOptionsValueForIndex<S>(target?.value, enumOptions, emptyValue)),
[onBlur, id]
);
const handleFocus = useCallback(
({ target }: FocusEvent<HTMLInputElement>) =>
onFocus(id, enumOptionsValueForIndex<S>(target?.value, enumOptions, emptyValue)),
[onFocus, id]
);
return (
<div className="checkboxes" id={id}>
{Array.isArray(enumOptions) &&
enumOptions.map((option, index) => {
const checked = enumOptionsIsSelected<S>(option.value, checkboxesValues);
const itemDisabled =
Array.isArray(enumDisabled) && enumDisabled.indexOf(option.value) !== -1;
const disabledCls = disabled || itemDisabled || readonly ? "disabled" : "";
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.checked) {
onChange(enumOptionsSelectValue<S>(index, checkboxesValues, enumOptions));
} else {
onChange(enumOptionsDeselectValue<S>(index, checkboxesValues, enumOptions));
}
};
const checkbox = (
// biome-ignore lint/correctness/useJsxKeyInIterable: it's wrapped
<span>
<input
type="checkbox"
id={optionId(id, index)}
name={id}
checked={checked}
value={String(index)}
disabled={disabled || itemDisabled || readonly}
autoFocus={autofocus && index === 0}
onChange={handleChange}
onBlur={handleBlur}
onFocus={handleFocus}
aria-describedby={ariaDescribedByIds<T>(id)}
/>
<span>{option.label}</span>
</span>
);
return inline ? (
<label key={index} className={`checkbox-inline ${disabledCls}`}>
{checkbox}
</label>
) : (
<div key={index} className={`checkbox ${disabledCls}`}>
<label>{checkbox}</label>
</div>
);
})}
</div>
);
}
export default CheckboxesWidget;