select-react-redux
Version:
A searchable select box, similar to select2.
216 lines (190 loc) • 6.9 kB
JavaScript
import React from 'react';
import {render} from 'react-dom';
import {Provider, connect} from 'react-redux';
import {createStore} from 'redux';
import actions from './actions';
import {reducers} from './reducers';
const NoItems = () => {
return (
<div
key={null}
className="item item-no-results"
>No results found</div>
);
};
const Presentation = ({...props}) => {
let topBar;
const visibleItems = Object.keys(props.visibleItems).map((item) => {
return (
<div
onClick={() => {
props.submit({
selected: item,
selectedItemLabel: props.items[item]
});
focus();
}}
key={item}
className={
(item == props.currentlyHighlighted) ? 'item item-selected' : 'item'
}
>
{props.items[item]}
</div>
)
});
const focus = () => {
if (topBar)
topBar.focus();
};
return (
<div
className="select-react-redux-container"
ref={(input) => {
if (props.initialRender && input) {
props.initialRenderFalse();
document.addEventListener('click', function (event) {
if (!input.contains(event.target)) {
props.refresh();
}
});
}
}}>
<a href="#"
tabIndex={props.tabIndex}
onClick={props.topBarOnClick}
onKeyPress={props.linkOnKeyPress}
onKeyDown={e => {
if (e.key.indexOf('Arrow') == 0) {
props.linkOnKeyPress(e)
}
}}
className={props.open ? 'selected selected-open' : 'selected'}
ref={function (input) {
if (input && props.open) {
topBar = input;
focus();
}
}}
>
<div
className={Object.keys(props.items).length == 0 ? 'top-bar top-bar-empty' : 'top-bar'}>
{ Object.keys(props.items).length == 0 ? 'No options available' :
props.selectedItemLabel
? props.selectedItemLabel
: 'Please select...'}
</div>
</a>
<div className={props.open ? 'results-container open' : 'results-container' }>
<div className="input-container">
<input
type="text"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
autoComplete="off"
ref={(item) => {
if (item && props.open) {
item.focus()
}
}}
value={props.visibilityFilter}
onKeyPress={(e) => {
if (e.key === 'Enter' && props.open) {
props.submit({
selected: props.currentlyHighlighted,
selectedItemLabel: props.items[props.currentlyHighlighted]
});
focus();
}
}}
onChange={props.inputOnChange}
onKeyDown={(e) => {
props.inputOnKeyDown(e);
if (e.key == 'Escape') {
focus();
}
}}
/>
</div>
{visibleItems.length > 0 ? visibleItems : <NoItems/>}
</div>
</div>
);
};
export const Select = ({items, selected = null, tabIndex = null, onChange}) => {
const store = createStore(reducers);
store.dispatch({type: '@@redux/INIT'});
window.store = store;
store.dispatch({type: actions.SET_ITEMS, payload: items});
if (selected) {
store.dispatch({
type: actions.SET_SELECTED, payload: {
selected: selected,
selectedItemLabel: items.selected
}
});
}
if (tabIndex) {
store.dispatch({type: actions.SET_TABINDEX, payload: tabIndex})
}
const mapStateToProps = (state = {}) => {
return state;
};
const mapDispatchToProps = (dispatch) => {
return {
submit: (item) => {
if (item.selected) {
dispatch({type: actions.SET_SELECTED, payload: item});
dispatch({type: actions.SET_OPEN, payload: false});
dispatch({type: actions.SET_FILTER, payload: ''});
onChange(item.selected);
}
},
linkOnKeyPress: (e) => {
if (e.key != 'Escape') {
dispatch({type: actions.SET_OPEN, payload: true});
dispatch({type: actions.SET_FILTER, payload: ''});
}
},
inputOnChange: (e) => {
dispatch({type: actions.SET_FILTER, payload: e.target.value})
},
inputOnKeyDown: (e) => {
if (e.key === 'ArrowDown') {
dispatch({type: actions.SET_NEXT_HIGHLIGHTED, payload: false})
}
if (e.key === 'ArrowUp') {
dispatch({type: actions.SET_PREV_HIGHLIGHTED, payload: false})
}
if (e.key === 'Escape') {
dispatch({type: actions.SET_OPEN, payload: false});
dispatch({type: actions.SET_FILTER, payload: ''});
}
},
topBarOnClick: () => {
dispatch({type: actions.TOGGLE_OPEN});
},
initialRenderFalse: () => {
dispatch({type: actions.SET_INITIAL_RENDER_FALSE});
},
refresh: () => {
dispatch({type: actions.SET_OPEN, payload: false});
dispatch({type: actions.SET_FILTER, payload: ''});
}
}
};
const SelectWithStore = connect(
mapStateToProps,
mapDispatchToProps
)(Presentation);
const render = (store) => {
return (
<SelectWithStore store={store}/>
)
};
store.subscribe(() => {
render(store);
});
render();
};