src/containers/Options.jsx
import browser from 'webextension-polyfill';
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
import getMessage from '../utils/i18n';
import { defaultOrder } from '../reducers/options';
const dragIcon = browser.extension.getURL('images/drag.png');
const SortableItem = SortableElement(({ value }) => ((
<li className="sortableListItem">
<img src={dragIcon} className="dragIcon" alt="drag" />
{value}
</li>
)));
const SortableList = SortableContainer(({ items }) => ((
<ul className="sortableList">
{items.map((value, index) => (
<SortableItem key={`item-${value}`} index={index} value={value} />
))}
</ul>
)));
class Options extends React.Component {
static get propTypes() {
return {
popupWidth: PropTypes.number.isRequired,
orderOfCandidates: PropTypes.arrayOf(PropTypes.string).isRequired,
maxResultsForEmpty: PropTypes.objectOf(PropTypes.number).isRequired,
enabledCJKMove: PropTypes.bool.isRequired,
hatenaUserName: PropTypes.string.isRequired,
theme: PropTypes.string.isRequired,
handlePopupWidthChange: PropTypes.func.isRequired,
handleMaxResultsForEmptyChange: PropTypes.func.isRequired,
handleCJKMoveChange: PropTypes.func.isRequired,
handleSortEnd: PropTypes.func.isRequired,
handleHatenaUserNameChange: PropTypes.func.isRequired,
handleThemeChange: PropTypes.func.isRequired,
};
}
handlePopupWidthChange(e) {
if (!Number.isNaN(e.target.valueAsNumber)) {
this.props.handlePopupWidthChange(e.target.valueAsNumber);
}
}
renderInputsOfCandidates() {
return defaultOrder.map((type) => {
const n = this.props.maxResultsForEmpty[type];
return (
<div key={`maxResultsFor-${type}`}>
<span className="maxResultsInputLabel">{type}</span>
<input
className="maxResultsInput"
type="number"
min="0"
max="20"
step="1"
value={n}
onChange={e => this.props.handleMaxResultsForEmptyChange({
[type]: parseInt(e.target.value, 10),
})}
/>
</div>
);
});
}
renderPopupWidthInput() {
return (
<div>
<h4 className="optionsLabel">Popup width</h4>
<input
className="optionsValueInput popupWidthInput"
type="number"
min="200"
max="5000"
step="50"
placeholder={getMessage('optionsUI_width')}
value={this.props.popupWidth}
onChange={e => this.handlePopupWidthChange(e)}
/>
</div>
);
}
renderOrderOfCandidates() {
return (
<div>
<h4 className="optionsLabel">Order of candidates</h4>
<p className="optionsDescription">You can change order by drag</p>
<SortableList items={this.props.orderOfCandidates} onSortEnd={this.props.handleSortEnd} />
</div>
);
}
renderMaxResultsForEmpty() {
return (
<div>
<h4 className="optionsLabel">Max results of candidates for empty query</h4>
<div className="optionsValue">
{this.renderInputsOfCandidates()}
</div>
</div>
);
}
renderKeyBindings() {
return (
<div>
<h4 className="optionsLabel">key-bindings</h4>
<input
className="optionsValueInput cjkMoveCheckbox"
type="checkbox"
checked={this.props.enabledCJKMove}
onChange={e => this.props.handleCJKMoveChange(e.target.checked)}
/>
C-j ... next-candidate, C-k ... previous-candidate
</div>
);
}
renderHatenaUserName() {
return (
<div>
<h4 className="optionsLabel">Hatena User Name</h4>
<input
className="optionsValueTextInput"
type="text"
value={this.props.hatenaUserName}
onChange={e => this.props.handleHatenaUserNameChange(e.target.value)}
/>
</div>
);
}
renderTheme() {
return (
<div>
<h4 className="optionsLabel">Select theme</h4>
<select
className="themeSelect"
value={this.props.theme}
onChange={e => this.props.handleThemeChange(e.target.value)}
>
<option value="">Default</option>
<option value="simple-dark">Simple Dark</option>
</select>
</div>
);
}
render() {
return (
<div className="options">
<h3 className="optionsTitle">Options</h3>
{this.renderPopupWidthInput()}
{this.renderOrderOfCandidates()}
{this.renderMaxResultsForEmpty()}
{this.renderKeyBindings()}
{this.renderHatenaUserName()}
{this.renderTheme()}
</div>
);
}
}
function mapStateToProps(state) {
return {
popupWidth: state.popupWidth,
orderOfCandidates: state.orderOfCandidates,
maxResultsForEmpty: state.maxResultsForEmpty,
enabledCJKMove: state.enabledCJKMove,
hatenaUserName: state.hatenaUserName,
theme: state.theme,
};
}
function mapDispatchToProps(dispatch) {
return {
handlePopupWidthChange: payload => dispatch({ type: 'POPUP_WIDTH', payload }),
handleSortEnd: payload => dispatch({ type: 'CHANGE_ORDER', payload }),
handleMaxResultsForEmptyChange: payload => dispatch({
type: 'UPDATE_MAX_RESULTS_FOR_EMPTY',
payload,
}),
handleCJKMoveChange: payload => dispatch({
type: 'ENABLE_CJK_MOVE',
payload,
}),
handleHatenaUserNameChange: payload => dispatch({ type: 'HATENA_USER_NAME', payload }),
handleThemeChange: payload => dispatch({ type: 'SET_THEME', payload }),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Options);