139 lines
3.6 KiB
TypeScript
139 lines
3.6 KiB
TypeScript
import React, { useEffect, useState, useCallback } from "react";
|
|
import Select from "../common/Select";
|
|
import Option from "../common/option";
|
|
import { GeneralIdRef, MakeGeneralIdRef } from "../../utils/GeneralIdRef";
|
|
import glossariesService, {
|
|
CustomFieldValue,
|
|
SystemGlossaries,
|
|
} from "../../modules/manager/glossary/services/glossaryService";
|
|
import MultiSelect from "../common/MultiSelect";
|
|
|
|
interface GlossaryPickerProps {
|
|
includeLabel?: boolean;
|
|
name: string;
|
|
label: string;
|
|
rootItem?: GeneralIdRef;
|
|
error?: string;
|
|
values: CustomFieldValue[];
|
|
maxEntries?: number;
|
|
onChange?: (name: string, values: CustomFieldValue[]) => void;
|
|
}
|
|
|
|
export default function GlossaryPicker({
|
|
includeLabel,
|
|
name,
|
|
label,
|
|
rootItem,
|
|
error,
|
|
values,
|
|
maxEntries,
|
|
onChange,
|
|
}: GlossaryPickerProps) {
|
|
const [options, setOptions] = useState<Option[]>([]);
|
|
const [selectedOptions, setSelectedOptions] = useState<Option[]>([]);
|
|
|
|
useEffect(() => {
|
|
async function load() {
|
|
const actualRootItem = rootItem ?? SystemGlossaries;
|
|
const glossary = await glossariesService.getGlossaryItem(actualRootItem);
|
|
|
|
if (glossary) {
|
|
const opts: Option[] = glossary.children.map(
|
|
(x: { id: any; name: any }) => ({
|
|
_id: x.id,
|
|
name: x.name,
|
|
}),
|
|
);
|
|
|
|
const selected: Option[] = [];
|
|
|
|
if (values) {
|
|
for (const option of values) {
|
|
const foundOption = opts.filter(
|
|
(x) =>
|
|
Number(x._id) === Number((option.value as GeneralIdRef).id),
|
|
)[0];
|
|
if (foundOption) selected.push(foundOption);
|
|
}
|
|
}
|
|
|
|
setOptions(opts);
|
|
setSelectedOptions(selected);
|
|
}
|
|
}
|
|
|
|
load();
|
|
}, [rootItem, values]);
|
|
|
|
const doOnChange = useCallback(
|
|
(newSelectedOptions: Option[]) => {
|
|
const vals: CustomFieldValue[] = newSelectedOptions.map((x) => ({
|
|
value: MakeGeneralIdRef(x._id as unknown as bigint),
|
|
displayValue: x.name,
|
|
}));
|
|
|
|
if (onChange) onChange(name, vals);
|
|
},
|
|
[onChange, name],
|
|
);
|
|
|
|
const handleChange = useCallback(
|
|
(e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
const input = e.currentTarget;
|
|
const id: number = Number(input.value);
|
|
|
|
const newSelected = options.filter((x) => x._id === id);
|
|
setSelectedOptions(newSelected);
|
|
doOnChange(newSelected);
|
|
},
|
|
[options, doOnChange],
|
|
);
|
|
|
|
const handleAdd = useCallback(
|
|
(item: Option) => {
|
|
const newSelected = [...selectedOptions, item];
|
|
setSelectedOptions(newSelected);
|
|
doOnChange(newSelected);
|
|
},
|
|
[selectedOptions, doOnChange],
|
|
);
|
|
|
|
const handleDelete = useCallback(
|
|
(item: Option) => {
|
|
const newSelected = selectedOptions.filter((x) => x !== item);
|
|
setSelectedOptions(newSelected);
|
|
doOnChange(newSelected);
|
|
},
|
|
[selectedOptions, doOnChange],
|
|
);
|
|
|
|
if (maxEntries === 1) {
|
|
const value = selectedOptions[0]?._id;
|
|
return (
|
|
<Select
|
|
includeLabel={includeLabel}
|
|
name={name}
|
|
label={label}
|
|
error={error}
|
|
value={value}
|
|
options={options}
|
|
includeBlankFirstEntry={true}
|
|
onChange={handleChange}
|
|
/>
|
|
);
|
|
} else {
|
|
return (
|
|
<MultiSelect
|
|
includeLabel={includeLabel}
|
|
name={name}
|
|
label={label}
|
|
error={error}
|
|
options={options}
|
|
selectedOptions={selectedOptions}
|
|
onAdd={handleAdd}
|
|
onDelete={handleDelete}
|
|
/>
|
|
);
|
|
}
|
|
}
|