I came back to this problem of trying to increment/decrement through a Menu and about to drive my head though wall figuring this out. I feel I'm almost there, but missing something. I am running into not being able to arrow right until I click on a button as well the button I clicked doesn't remove the class. Any help would much appreciated.
Button js:
...
class Button extends Component {
onClick() {
const { label, onClick } = this.props;
onClick(label);
}
render() {
const {
onClick,
props: { activeTab, label, tab, className }
} = this;
let ariaSelected = "";
if (activeTab === label || className === "active") {
ariaSelected += "true";
}
return (
<li role="presentation">
<a
className={className}
aria-selected={ariaSelected ? "true" : undefined}
onClick={e => this.onClick(e)}
role="tab"
id={"tab" + tab}
//tabIndex="-1"
>
{label}
</a>
</li>
);
}
}
..
Menu Js:
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
activeTab: this.props.children[0].props.label,
cursor: 0
};
this.handleKeyDown = this.handleKeyDown.bind(this);
}
componentDidMount() {
document.addEventListener("keydown", this.handleKeyDown, false);
}
componentWillUnmount() {
document.removeEventListener("keydown", this.handleKeyDown, false);
}
handleKeyDown(e) {
const { cursor } = this.state;
const cnt = React.Children.count(this.props.children);
if (e.keyCode === 37 && cursor > 0) {
this.setState(prevState => ({
cursor: prevState.cursor - 1
}));
console.log(cursor);
} else if (e.keyCode === 39 && cursor < cnt - 1) {
this.setState(prevState => ({
cursor: prevState.cursor + 1
}));
console.log(cursor);
}
}
onClickTabItem = tab => {
this.setState({
activeTab: tab
});
};
render() {
const {
onClickTabItem,
props: { children },
state: { activeTab, cursor, className }
} = this;
return (
<div className="tabbed">
<ul role="tablist">
{children.map((child, i) => {
const { label, className } = child.props;
return (
<Tab
activeTab={activeTab}
key={label}
label={label}
onClick={onClickTabItem}
tab={i}
className={ cursor === i || activeTab === label ? "active" : null}
/>
);
})}
</ul>
<div className="tab-content">
{children.map(child => {
//if tab has label or active set, otherwise do nohthing
if (child.props.label !== activeTab) return undefined;
return child.props.children;
})}
</div>
</div>
);
}
}
You could grab the label number from activeTab instead of having a cursor variable for when arrow keys are pressed:
handleKeyDown(e) {
const cnt = React.Children.count(this.props.children);
const pos = ~~this.state.activeTab[8] // get current position
if (e.keyCode === 37 && pos > 1) {
this.setState({
activeTab: "Section " + (pos - 1)
});
} else if (e.keyCode === 39 && pos < cnt) {
this.setState({
activeTab: "Section " + (pos + 1)
});
}
}
and then where className is set change cursor === i || activeTab === label ? "active" : null
to just activeTab === label ? "active" : null
edit: This way is a little cleaner, in case you plan on changing section titles, which I imagine you might.
handleKeyDown(e) {
const labels = this.props.children.map((child) => {
return child.props.label;
});
const cnt = labels.length;
const pos = labels.indexOf(this.state.activeTab); // get current position
if (e.keyCode === 37 && pos > 0) {
this.setState({
activeTab: labels[pos - 1]
});
} else if (e.keyCode === 39 && pos < cnt - 1) {
this.setState({
activeTab: labels[pos + 1]
});
}
}