2022-03-16 06:26:15 +00:00
|
|
|
import {
|
2021-03-25 14:22:43 +00:00
|
|
|
FocusEvent,
|
|
|
|
FunctionComponent,
|
|
|
|
KeyboardEvent,
|
|
|
|
useCallback,
|
2021-12-09 12:44:17 +00:00
|
|
|
useEffect,
|
2021-03-25 14:22:43 +00:00
|
|
|
useMemo,
|
|
|
|
useRef,
|
|
|
|
useState,
|
|
|
|
} from "react";
|
|
|
|
|
2021-06-28 10:36:54 +00:00
|
|
|
const SplitKeys = ["Tab", "Enter", " ", ",", ";"];
|
2021-03-25 14:22:43 +00:00
|
|
|
|
|
|
|
export interface ChipsProps {
|
|
|
|
disabled?: boolean;
|
2021-12-10 16:11:01 +00:00
|
|
|
defaultValue?: readonly string[];
|
|
|
|
value?: readonly string[];
|
2021-03-25 14:22:43 +00:00
|
|
|
onChange?: (v: string[]) => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const Chips: FunctionComponent<ChipsProps> = ({
|
|
|
|
defaultValue,
|
2021-12-09 12:44:17 +00:00
|
|
|
value,
|
2021-03-25 14:22:43 +00:00
|
|
|
disabled,
|
|
|
|
onChange,
|
|
|
|
}) => {
|
2021-12-10 16:11:01 +00:00
|
|
|
const [chips, setChips] = useState<Readonly<string[]>>(() => {
|
2021-12-09 12:44:17 +00:00
|
|
|
if (value) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
if (defaultValue) {
|
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
return [];
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (value) {
|
|
|
|
setChips(value);
|
|
|
|
}
|
|
|
|
}, [value]);
|
2021-03-25 14:22:43 +00:00
|
|
|
|
|
|
|
const input = useRef<HTMLInputElement>(null);
|
|
|
|
|
|
|
|
const addChip = useCallback(
|
|
|
|
(value: string) => {
|
2021-08-23 03:35:04 +00:00
|
|
|
setChips((cp) => {
|
|
|
|
const newChips = [...cp, value];
|
2021-09-01 03:30:28 +00:00
|
|
|
onChange && onChange(newChips);
|
2021-08-23 03:35:04 +00:00
|
|
|
return newChips;
|
|
|
|
});
|
2021-03-25 14:22:43 +00:00
|
|
|
},
|
2021-09-01 03:30:28 +00:00
|
|
|
[onChange]
|
2021-03-25 14:22:43 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const removeChip = useCallback(
|
|
|
|
(idx?: number) => {
|
2021-08-23 03:35:04 +00:00
|
|
|
setChips((cp) => {
|
|
|
|
const index = idx ?? cp.length - 1;
|
|
|
|
if (index !== -1) {
|
|
|
|
const newChips = [...cp];
|
|
|
|
newChips.splice(index, 1);
|
2021-09-01 03:30:28 +00:00
|
|
|
onChange && onChange(newChips);
|
2021-08-23 03:35:04 +00:00
|
|
|
return newChips;
|
|
|
|
} else {
|
|
|
|
return cp;
|
|
|
|
}
|
|
|
|
});
|
2021-03-25 14:22:43 +00:00
|
|
|
},
|
2021-09-01 03:30:28 +00:00
|
|
|
[onChange]
|
2021-03-25 14:22:43 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const clearInput = useCallback(() => {
|
|
|
|
if (input.current) {
|
|
|
|
input.current.value = "";
|
|
|
|
}
|
|
|
|
}, [input]);
|
|
|
|
|
|
|
|
const onKeyUp = useCallback(
|
|
|
|
(event: KeyboardEvent<HTMLInputElement>) => {
|
|
|
|
const pressed = event.key;
|
|
|
|
const value = event.currentTarget.value;
|
|
|
|
if (SplitKeys.includes(pressed) && value.length !== 0) {
|
|
|
|
event.preventDefault();
|
|
|
|
addChip(value);
|
|
|
|
clearInput();
|
|
|
|
} else if (pressed === "Backspace" && value.length === 0) {
|
|
|
|
event.preventDefault();
|
|
|
|
removeChip();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[addChip, removeChip, clearInput]
|
|
|
|
);
|
|
|
|
|
|
|
|
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {
|
|
|
|
const pressed = event.key;
|
|
|
|
const value = event.currentTarget.value;
|
|
|
|
if (SplitKeys.includes(pressed) && value.length !== 0) {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const onBlur = useCallback(
|
|
|
|
(event: FocusEvent<HTMLInputElement>) => {
|
|
|
|
const value = event.currentTarget.value;
|
|
|
|
if (value.length !== 0) {
|
|
|
|
event.preventDefault();
|
|
|
|
addChip(value);
|
|
|
|
clearInput();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[addChip, clearInput]
|
|
|
|
);
|
|
|
|
|
|
|
|
const chipElements = useMemo(
|
|
|
|
() =>
|
|
|
|
chips.map((v, idx) => (
|
|
|
|
<span
|
|
|
|
key={idx}
|
|
|
|
title={v}
|
|
|
|
className={`custom-chip ${disabled ? "" : "active"}`}
|
|
|
|
onClick={() => {
|
|
|
|
if (!disabled) {
|
|
|
|
removeChip(idx);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{v}
|
|
|
|
</span>
|
|
|
|
)),
|
|
|
|
[chips, removeChip, disabled]
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="form-control custom-chip-input d-flex">
|
|
|
|
<div className="chip-container">{chipElements}</div>
|
|
|
|
<input
|
|
|
|
disabled={disabled}
|
|
|
|
className="main-input p-0"
|
|
|
|
ref={input}
|
|
|
|
onKeyUp={onKeyUp}
|
|
|
|
onKeyDown={onKeyDown}
|
|
|
|
onBlur={onBlur}
|
|
|
|
></input>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|