This extracts input with chips from the NTP servers into a separate reusable ChippingInput component.
118 lines
2.9 KiB
TypeScript
118 lines
2.9 KiB
TypeScript
import React, { useState } from 'react';
|
|
|
|
import {
|
|
Button,
|
|
Chip,
|
|
ChipGroup,
|
|
HelperText,
|
|
HelperTextItem,
|
|
TextInputGroup,
|
|
TextInputGroupMain,
|
|
TextInputGroupUtilities,
|
|
} from '@patternfly/react-core/dist/esm';
|
|
import { PlusCircleIcon, TimesIcon } from '@patternfly/react-icons';
|
|
import { UnknownAction } from 'redux';
|
|
|
|
import { useAppDispatch } from '../../store/hooks';
|
|
|
|
type ChippingInputProps = {
|
|
ariaLabel: string;
|
|
placeholder: string;
|
|
validator: (value: string) => boolean;
|
|
list: string[] | undefined;
|
|
item: string;
|
|
addAction: (value: string) => UnknownAction;
|
|
removeAction: (value: string) => UnknownAction;
|
|
};
|
|
|
|
const ChippingInput = ({
|
|
ariaLabel,
|
|
placeholder,
|
|
validator,
|
|
list,
|
|
item,
|
|
addAction,
|
|
removeAction,
|
|
}: ChippingInputProps) => {
|
|
const dispatch = useAppDispatch();
|
|
|
|
const [inputValue, setInputValue] = useState('');
|
|
const [errorText, setErrorText] = useState('');
|
|
|
|
const onTextInputChange = (
|
|
_event: React.FormEvent<HTMLInputElement>,
|
|
value: string
|
|
) => {
|
|
setInputValue(value);
|
|
};
|
|
|
|
const handleKeyDown = (e: React.KeyboardEvent, value: string) => {
|
|
if (e.key === ' ' || e.key === 'Enter' || e.key === ',') {
|
|
e.preventDefault();
|
|
|
|
if (validator(value) && !list?.includes(value)) {
|
|
dispatch(addAction(value));
|
|
setInputValue('');
|
|
setErrorText('');
|
|
}
|
|
|
|
if (list?.includes(value)) {
|
|
setErrorText(`${item} already exists.`);
|
|
}
|
|
|
|
if (!validator(value)) {
|
|
setErrorText('Invalid format.');
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleAddItem = (e: React.MouseEvent, value: string) => {
|
|
dispatch(addAction(value));
|
|
setInputValue('');
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<TextInputGroup>
|
|
<TextInputGroupMain
|
|
placeholder={placeholder}
|
|
onChange={onTextInputChange}
|
|
value={inputValue}
|
|
onKeyDown={(e) => handleKeyDown(e, inputValue)}
|
|
/>
|
|
<TextInputGroupUtilities>
|
|
<Button
|
|
variant="plain"
|
|
onClick={(e) => handleAddItem(e, inputValue)}
|
|
isDisabled={!inputValue}
|
|
aria-label={ariaLabel}
|
|
>
|
|
<PlusCircleIcon className="pf-v5-u-primary-color-100" />
|
|
</Button>
|
|
<Button
|
|
variant="plain"
|
|
onClick={() => setInputValue('')}
|
|
isDisabled={!inputValue}
|
|
aria-label="Clear input"
|
|
>
|
|
<TimesIcon />
|
|
</Button>
|
|
</TextInputGroupUtilities>
|
|
</TextInputGroup>
|
|
{errorText && (
|
|
<HelperText>
|
|
<HelperTextItem variant={'error'}>{errorText}</HelperTextItem>
|
|
</HelperText>
|
|
)}
|
|
<ChipGroup numChips={5} className="pf-v5-u-mt-sm pf-v5-u-w-100">
|
|
{list?.map((item) => (
|
|
<Chip key={item} onClick={() => dispatch(removeAction(item))}>
|
|
{item}
|
|
</Chip>
|
|
))}
|
|
</ChipGroup>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ChippingInput;
|