Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Navigate a material-ui list with arrow keys

I'm using material-ui to make an electron application. Some screens are master-detail and I'm using a list to show the overview. I would like to make it possible to navigate this list with arrow keys. Is there a builtin option to do this?

If it is not built in, what is the best approach to make this?

Update: I made my own component for now. Not sure if it is the best solution, but seems to work:

export default function NavigateList(props) {
    const { children, data, ...other } = props;
    const elements = data.map((val, i) => children(val, i));

    function gotoPrevElement() {
        const selected = elements.findIndex(e => e.props.selected);
        if (selected > 0) {
            const el = elements[selected - 1];
            el.props.onClick(data[selected - 1]);
        }
    }
    function gotoNextElement() {
        const selected = elements.findIndex(e => e.props.selected);
        if (selected > -1 && selected < elements.length - 1) {
            const el = elements[selected + 1];
            el.props.onClick(data[selected + 1]);
        }
    }

    function handleKey(e) {
        if (e.key === "ArrowDown") {
            gotoNextElement();
        }
        if (e.key === "ArrowUp") {
            gotoPrevElement();
        }
    }

    return (
        <List onKeyDown={handleKey} {...other}>
            {elements}
        </List>
    );
}

Here is an example how it can be used:

<NavigateList data={people}>
    {(p, i) => (
        <ListItem
            button
            key={i}
            selected={checkIfSelected(p)}
            onClick={e => setSelected(p)}
        >
            <ListItemText
                primary={p.primary}
                secondary={p.secondary}
            />
        </ListItem>
    )}
</NavigateList>
like image 783
roeland Avatar asked Nov 25 '18 15:11

roeland


1 Answers

You could use a List for the master-interface and a Card for the detail-interface.

Your parent component will handle List selection changes from your master-interface and be responsible for sending the correct detail data to the Card.

Here's an example of how this structure would look like using Material-UI components:

class ParentComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            currentDetailIndex: 0,
            numOfListItems: 10,
            detailData: [
                {...},
                {...},
                ...
            ]
        };
    }

    changeDetailIndex = (newIndex) => {
        this.setState({ currentDetailIndex: newIndex });
    }

    moveUp = () => {
        if (this.state.currentDetailIndex > 0) {
            this.setState({ currentDetailIndex: this.state.currentDetailIndex - 1 });
        }
    }

    moveDown = () => {
        if (this.state.currentDetailIndex < this.state.numOfListItems - 1) {
            this.setState({ currentDetailIndex: this.state.currentDetailIndex + 1 });
        }
    }

    onKeyPressed = (e) => {
        if (e.keyCode == '38') {
            // up arrow
            this.moveUp();
        }
        else if (e.keyCode == '40') {
            // down arrow
            this.moveDown();
        }
    }

    render() {
        return (
            <div>
                <List component="nav" onKeyDown={this.onKeyPressed}>
                    <ListItem onClick={() => { this.changeDetailIndex(someIndex); }}>'s... 
                </List>
                <Card>
                    <CardContent>
                        <SomeDetailComponent data={this.state.detailData[this.state.currentDetailIndex]} />
                    </CardContent>
                </Card>
            </div>
        );
    };
}
like image 108
Shawn Andrews Avatar answered Oct 06 '22 11:10

Shawn Andrews