Lightning Web Components – Shift Select Multiple Checkboxes in LWC

I have developed a custom dual list box component using LWC. I wish to add the functionality of shift selecting multiple checkboxes. Eg: If I select 1st checkbox and shift select 5th checkbox, then the first five checkboxes must be checked.

Here's a screenshot of the component for better understanding:
screenshot
The buttons on the component act on the checked checkboxes.

How can I achieve shift select of multiple checkboxes? I did research but I did not find anything useful. There was nothing in salesforce documentation too. Please guide. Thank You!

HTML CODE for checkbox group:

<!-- Defining the available fields checkbox group -->
<lightning-checkbox-group name="Available Fields" label="Available Fields"
        options={availableOptionsForObjectFieldsCopy} value={availableCheckedValues}
        onchange={handleSelectionOfAvailableValues}>
</lightning-checkbox-group>

<!-- Defining the selected fields checkbox group -->
<lightning-checkbox-group name="Selected Fields" label="Selected Fields"
        options={selectedOptionsForObjectFieldsCopy} value={selectedCheckedValues}
        onchange={handleSelectionOfSelectedValues}>
</lightning-checkbox-group>

Best Answer

You probably should not rely on the lightning-checkbox-group base component here.

I implemented a change handler method that looks at the pre-existing value and the new value from the change event, filling in all the indexes in between the furthest selected extreme (start or end) of the currently selected options and the newly-checked value. *I had to make some assumptions about the business requirements -- your actual implementation rules may vary.

@track checkedValues = [];
shiftKey = false;

handleKeyDown(event) {
    this.shiftKey = event.keyCode === 16;
}

handleKeyUp(event) {
    this.shiftKey = event.keyCode !== 16;
}

handleChange(event) {
    // If the shift key is down, fill in options between the newly checked value
    // and the current range's furthest extreme
    if (this.shiftKey && this.checkedValues.length > 0) {
        let currRange = this.getOptionIdxRange(this.checkedValues);
        let newRange = this.getOptionIdxRange(event.target.value);
        let optionValues = this.options.map(o => o.value);
        // If the newly added option is before the start of the current range,
        // add all values between that and the current start
        if (currRange.startIdx > newRange.startIdx) {
            this.checkedValues = optionValues
                .filter((o, idx) => {
                    return (
                        this.checkedValues.includes(o) 
                        || (
                            idx >= newRange.startIdx
                            && idx < currRange.startIdx
                        )
                    )
                });
        // If the newly added option is after the end of the current range,
        // add all values between the current end and the new end
        } else if (currRange.endIdx < newRange.endIdx) {
            this.checkedValues = optionValues
                .filter((o, idx) => {
                    return (
                        this.checkedValues.includes(o) 
                        || (
                            idx <= newRange.endIdx
                            && idx > currRange.endIdx
                        )
                    )
                });
        }
    } else {
        this.checkedValues = event.target.value;
    }
}

getOptionIdxRange(values) {
    let range = {startIdx: -1, endIdx: -1};
    let startValue = values[0];
    let endValue = values[values.length - 1];
    this.options.forEach((o, idx) => {
        if (o.value === startValue) {
            range.startIdx = idx;
        } 
        if (o.value === endValue) {
            range.endIdx = idx;
        }
    });
    return range;
}

It works fine, except that lightning-checkbox-group has some spotty behavior when it comes to updating its value attribute on Shift+click. It seems to have something to do with how quickly you click it, which is weird.

So... if I were you, I would use something like this Playground example.

https://developer.salesforce.com/docs/component-library/tools/playground/NTazGM8Jw/11/edit

But instead of the lightning-checkbox-group, I would try rolling a custom multi-checkbox component in lieu of the OOTB base component from Salesforce.