Skip to content

Instantly share code, notes, and snippets.

@197291
Last active June 19, 2019 07:55
Show Gist options
  • Save 197291/6915d3b439c3b8e2b23ff6c5182aa91d to your computer and use it in GitHub Desktop.
Save 197291/6915d3b439c3b8e2b23ff6c5182aa91d to your computer and use it in GitHub Desktop.
import * as React from 'react';
import { KeyCodeMap } from 'constants/core/key-code-map';
import { getComponentDisplayName } from 'helpers/common/get-component-display-name';
interface State {
selectedRowIndex: number;
mouseIndex: number;
scrollElement: any;
rowsLength: number;
}
interface Data {
shouldSelectOnHover?: boolean;
ROW_HEIGHT: number;
DROPDOWN_HEIGHT: number;
}
export interface SelectKeyEventsProps {
selectedRowIndex?: number;
selectRow?(event: React.KeyboardEvent<HTMLInputElement>);
resetRow?();
setIndex?(index: number);
setRowsLength?(length: number);
setScrollElement?(scrollElement: any);
}
export interface SelectKeyEvents {
input?: HTMLInputElement;
scrollElement: any;
selectOption(index: number): void;
closeDropdown?();
}
export default function selectKeyEvents<P extends SelectKeyEventsProps>(
WrappedComponent: React.ComponentType<P>,
data: Data
) {
class WithSelectKeyEvents extends React.Component<P, State> {
state = {
selectedRowIndex: -1,
mouseIndex: null,
scrollElement: null,
rowsLength: 0
};
instance;
static displayName: string;
proc(wrappedComponentInstance: any) {
this.instance = wrappedComponentInstance;
}
setRowsLength = (rowsLength: number) => {
this.setState({ rowsLength });
};
setScrollElement = (scrollElement: any) => {
this.setState({ scrollElement });
};
setIndex = (index: number) => {
this.setState((state) => ({
mouseIndex: index,
selectedRowIndex: data.shouldSelectOnHover ? index : state.selectedRowIndex
}));
};
resetRow = () => {
this.setState({ selectedRowIndex: -1 });
};
selectRow = (event: React.KeyboardEvent<HTMLInputElement>) => {
const key = event.keyCode;
if (
key !== KeyCodeMap.ArrowDown &&
key !== KeyCodeMap.ArrowUp &&
key !== KeyCodeMap.Enter &&
key !== KeyCodeMap.Escape &&
key !== KeyCodeMap.Tab
) {
return;
}
const { selectedRowIndex, mouseIndex, rowsLength, scrollElement } = this.state;
let index = selectedRowIndex;
if (mouseIndex !== null) {
index = mouseIndex;
this.setState({ mouseIndex: null });
}
if (key === KeyCodeMap.Enter && index !== -1) {
if (this.instance.input) {
this.instance.input.blur();
}
if (this.instance.selectOption) {
this.instance.selectOption(index);
}
event.preventDefault();
event.stopPropagation();
return;
}
if (key === KeyCodeMap.Tab || key === KeyCodeMap.Escape) {
if (this.instance.closeDropdown) {
this.instance.closeDropdown(index);
}
return;
}
if (key === KeyCodeMap.ArrowDown) {
++index;
}
if (key === KeyCodeMap.ArrowUp) {
--index;
}
index = index < 0 ? rowsLength - 1 : index === rowsLength ? 0 : index;
const scrollEl = scrollElement || this.instance.scrollElement;
if (!scrollEl) {
return;
}
const topOffset = scrollEl.getScrollTop();
const i0 = Math.ceil(topOffset / data.ROW_HEIGHT);
const i1 = i0 + Math.round(data.DROPDOWN_HEIGHT / data.ROW_HEIGHT) - 1;
if (index < i0) {
scrollEl.scrollTop(topOffset - data.ROW_HEIGHT);
}
if (index > i1) {
scrollEl.scrollTop(topOffset + data.ROW_HEIGHT);
}
if (index === 0) {
scrollEl.scrollTop(0);
}
if (index === rowsLength - 1) {
scrollEl.scrollTop(data.ROW_HEIGHT * rowsLength);
}
this.setState({ selectedRowIndex: index });
};
render() {
const props = Object.assign({}, this.props, { ref: this.proc.bind(this) });
return (
<WrappedComponent
{...props}
selectedRowIndex={this.state.selectedRowIndex}
selectRow={this.selectRow}
resetRow={this.resetRow}
setIndex={this.setIndex}
setRowsLength={this.setRowsLength}
setScrollElement={this.setScrollElement}
/>
);
}
}
WithSelectKeyEvents.displayName = `WithSelectKeyEvents(${getComponentDisplayName(
WrappedComponent
)})`;
return WithSelectKeyEvents;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment