debian-image-builder-frontend/src/Components/CreateImageWizard/ChippingInput.tsx
regexowl ae795c3b6a Wizard: Extract ChippingInput to reusable component
This extracts input with chips from the NTP servers into a separate reusable ChippingInput component.
2024-12-16 12:12:56 -06:00

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;