2

I have a react-select component (which is generated using CreatableSelect), it is a multi select text input which allows users to add keywords as options.

It's working fine but I need a way to allow users to copy some comma separated text and paste it into the component so that each item is going to be added as a single option.

For instance, if the text is "123,456,789", the expected output will be 3 individual options: 123, 456 and 789, respectively.

Here's my component

import React, {KeyboardEventHandler} from 'react';
import CreatableSelect from 'react-select/creatable';
import { ActionMeta, OnChangeValue } from 'react-select';

const MultiSelectTextInput = (props) => {
    const components = {
        DropdownIndicator: null,
    };

    interface Option {
        readonly label: string;
        readonly value: string;
    }

    const createOption = (label: string) => ({
        label,
        value: label,
    });

    const handleChange = (value: OnChangeValue<Option, true>, actionMeta: ActionMeta<Option>) => {
        console.group('Value Changed');
        console.log(value);
        console.log(`action: ${actionMeta.action}`);
        console.groupEnd();
        props.setValue(value);
    };

    const handleInputChange = (inputValue: string) => {
        props.setInputValue(inputValue);
    };

    const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
        if (!props.inputValue) return;

        switch (event.key) {
            case 'Enter':
            case 'Tab':
                if (props.value.map(v => v.label).includes(props.inputValue)) {
                    console.log('Value Already Exists!')
                    props.setInputValue('');
                }
                else {
                    console.group('Value Added');
                    console.log(props.value);
                    console.groupEnd();
                    props.setInputValue('');
                    props.setValue([...props.value, createOption(props.inputValue)])
                }

                event.preventDefault();
        }
    };

    return (
        <CreatableSelect
            id={props.id}
            instanceId={props.id}
            className="w-100"
            components={components}
            inputValue={props.inputValue}
            isClearable
            isMulti
            menuIsOpen={false}
            onChange={handleChange}
            onInputChange={handleInputChange}
            onKeyDown={handleKeyDown}
            placeholder={props.placeholder}
            value={props.value}
        />
    );
};

export default MultiSelectTextInput;

I call this component from a page (of a next.js project) like below. The component modifies the state of the page in which it is called.

...
import MultiSelectTextInput from "../components/Form/MultiSelect/MultiSelectTextInput";
...

const NcciLite = () => {
    const [value, setValue] = useState<any>([]);
    const [inputValue, setInputValue] = useState<any>('');
    ...
    return (
        <React.Fragment>
            <Container fluid={true}>
                <Breadcrumbs title="Tools" breadcrumbItem="NCCI Lite" />
                <Row>
                    <Col>
                        <Card>
                            <CardBody>
                                <CardTitle className="mb-4 fw-light">...</CardTitle>
                                <Form onSubmit={event => event.preventDefault()}>
                                   ...
                                    <Row className="mb-4">
                                        <Col>
                                            <div className="d-inline-flex col-md-9">
                                                <MultiSelectTextInput
                                                    id="codes"
                                                    value={value}
                                                    setValue={setValue}
                                                    inputValue={inputValue}
                                                    setInputValue={setInputValue}
                                                    placeholder="Type Next Code(s) or Paste Here"
                                                />
                                            </div>
                                        </Col>
                                    </Row>
                                   ...
                                </Form>
                            </CardBody>
                        </Card>
                    </Col>
                </Row>
            </Container>
        </React.Fragment>
};

export default NcciLite;

Any help would be appreciated

1 Answer 1

4

Allright, I will answer my own question. I thought it would be difficult but it was actually not. Just a few lines of javascript code.

Modified component

import React, {KeyboardEventHandler} from 'react';
import CreatableSelect from 'react-select/creatable';
import { ActionMeta, OnChangeValue } from 'react-select';

const MultiSelectTextInput = (props) => {
    const components = {
        DropdownIndicator: null,
    };

    interface Option {
        readonly label: string;
        readonly value: string;
    }

    const createOption = (label: string) => ({
        label,
        value: label,
    });

    const handleChange = (value: OnChangeValue<Option, true>, actionMeta: ActionMeta<Option>) => {
        // console.group('Value Changed');
        // console.log(value);
        // console.log(`action: ${actionMeta.action}`);
        // console.groupEnd();
        props.setValue(value);
    };

    const handleInputChange = (inputValue: string) => {
        props.setInputValue(inputValue);
    };

    const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
        if (!props.inputValue) return;

        switch (event.key) {
            case 'Enter':
            case 'Tab':
                if (props.value.map(v => v.label.trim()).includes(props.inputValue.trim())) {
                    // console.log('Value Already Exists!')
                    props.setInputValue('');
                }
                else {
                    // console.group('Value Added');
                    // console.log(props.value);
                    // console.groupEnd();
                    if (!props.customizedSetter && props.inputValue.trim().indexOf(',') > -1) {
                        props.setInputValue('');

                        const values = props.inputValue.trim().split(',').filter(iv => !props.value.map(v => v.label.trim()).includes(iv.trim()));

                        for (let i = 0; i < values.length; i++) {
                            props.setValue((oldValue) =>[...oldValue, createOption(values[i].trim())]);
                        }
                    }
                    else {
                        props.setInputValue('');
                        props.setValue([...props.value, createOption(props.inputValue.trim())]);
                    }
                }

                event.preventDefault();
        }
    };

    return (
        <CreatableSelect
            id={props.id}
            instanceId={props.id}
            className="cs w-100"
            components={components}
            inputValue={props.inputValue}
            isClearable
            isMulti
            menuIsOpen={false}
            onChange={handleChange}
            onInputChange={handleInputChange}
            onKeyDown={handleKeyDown}
            placeholder={props.placeholder}
            value={props.value}
        />
    );
};

export default MultiSelectTextInput;


Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.