diff options
author | Maximilian Hils <git@maximilianhils.com> | 2017-07-20 18:01:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-20 18:01:53 +0200 |
commit | e1d0bc6de975c377cb42022209ea1b5bce515433 (patch) | |
tree | 4f52c967b9312dd0c08e3f88f51b93dd2849dffe /web/src/js/components/Modal | |
parent | 8526ca9e175af4a903348c39928535ae10f7543d (diff) | |
parent | cb73658dd43e01bdedd84aa31601887b19c106ca (diff) | |
download | mitmproxy-e1d0bc6de975c377cb42022209ea1b5bce515433.tar.gz mitmproxy-e1d0bc6de975c377cb42022209ea1b5bce515433.tar.bz2 mitmproxy-e1d0bc6de975c377cb42022209ea1b5bce515433.zip |
Merge pull request #2430 from MatthewShao/mitmweb-options
[web] [WIP] Mitmweb options
Diffstat (limited to 'web/src/js/components/Modal')
-rw-r--r-- | web/src/js/components/Modal/Option.jsx | 141 | ||||
-rw-r--r-- | web/src/js/components/Modal/OptionMaster.jsx | 119 | ||||
-rw-r--r-- | web/src/js/components/Modal/OptionModal.jsx | 57 |
3 files changed, 177 insertions, 140 deletions
diff --git a/web/src/js/components/Modal/Option.jsx b/web/src/js/components/Modal/Option.jsx new file mode 100644 index 00000000..58b863d1 --- /dev/null +++ b/web/src/js/components/Modal/Option.jsx @@ -0,0 +1,141 @@ +import React, { Component } from "react" +import PropTypes from "prop-types" +import { connect } from "react-redux" +import { update as updateOptions } from "../../ducks/options" +import { Key } from "../../utils" +import classnames from 'classnames' + +const stopPropagation = e => { + if (e.keyCode !== Key.ESC) { + e.stopPropagation() + } +} + +BooleanOption.PropTypes = { + value: PropTypes.bool.isRequired, + onChange: PropTypes.func.isRequired, +} +function BooleanOption({ value, onChange, ...props }) { + return ( + <div className="checkbox"> + <label> + <input type="checkbox" + checked={value} + onChange={e => onChange(e.target.checked)} + {...props} + /> + Enable + </label> + </div> + ) +} + +StringOption.PropTypes = { + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, +} +function StringOption({ value, onChange, ...props }) { + return ( + <input type="text" + value={value || ""} + onChange={e => onChange(e.target.value)} + {...props} + /> + ) +} +function Optional(Component) { + return function ({ onChange, ...props }) { + return <Component + onChange={x => onChange(x ? x : null)} + {...props} + /> + } +} + +NumberOption.PropTypes = { + value: PropTypes.number.isRequired, + onChange: PropTypes.func.isRequired, +} +function NumberOption({ value, onChange, ...props }) { + return ( + <input type="number" + value={value} + onChange={(e) => onChange(parseInt(e.target.value))} + {...props} + /> + ) +} + +ChoicesOption.PropTypes = { + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, +} +export function ChoicesOption({ value, onChange, choices, ...props }) { + return ( + <select + onChange={(e) => onChange(e.target.value)} + selected={value} + {...props} + > + { choices.map( + choice => ( + <option key={choice} value={choice}>{choice}</option> + ) + )} + </select> + ) +} + +StringSequenceOption.PropTypes = { + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, +} +function StringSequenceOption({ value, onChange, ...props }) { + const height = Math.max(value.length, 1) + return <textarea + rows={height} + value={value.join('\n')} + onChange={e => onChange(e.target.value.split("\n"))} + {...props} + /> +} + +export const Options = { + "bool": BooleanOption, + "str": StringOption, + "int": NumberOption, + "optional str": Optional(StringOption), + "sequence of str": StringSequenceOption, +} + +function PureOption({ choices, type, value, onChange, name, error }) { + let Opt, props = {} + if (choices) { + Opt = ChoicesOption; + props.choices = choices + } else { + Opt = Options[type] + } + if (Opt !== BooleanOption) { + props.className = "form-control" + } + + return <div className={classnames({'has-error':error})}> + <Opt + name={name} + value={value} + onChange={onChange} + onKeyDown={stopPropagation} + {...props} + /> + </div> +} +export default connect( + (state, { name }) => ({ + ...state.options[name], + ...state.ui.optionsEditor[name] + }), + (dispatch, { name }) => ({ + onChange: value => dispatch(updateOptions(name, value)) + }) +)(PureOption) diff --git a/web/src/js/components/Modal/OptionMaster.jsx b/web/src/js/components/Modal/OptionMaster.jsx deleted file mode 100644 index c25dda72..00000000 --- a/web/src/js/components/Modal/OptionMaster.jsx +++ /dev/null @@ -1,119 +0,0 @@ -import PropTypes from 'prop-types' - -PureBooleanOption.PropTypes = { - value: PropTypes.bool.isRequired, - onChange: PropTypes.func.isRequired, -} - -function PureBooleanOption({ value, onChange, name, help}) { - return ( - <label> - { name } - <input type="checkbox" - checked={value} - onChange={onChange} - title={help} - /> - </label> - ) -} - -PureStringOption.PropTypes = { - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, -} - -function PureStringOption( { value, onChange, name, help }) { - let onKeyDown = (e) => {e.stopPropagation()} - return ( - <label> - { name } - <input type="text" - value={value} - onChange={onChange} - title={help} - onKeyDown={onKeyDown} - /> - </label> - ) -} - -PureNumberOption.PropTypes = { - value: PropTypes.number.isRequired, - onChange: PropTypes.func.isRequired, -} - -function PureNumberOption( {value, onChange, name, help }) { - let onKeyDown = (e) => {e.stopPropagation()} - return ( - <label> - { name } - <input type="number" - value={value} - onChange={onChange} - title={help} - onKeyDown={onKeyDown} - /> - </label> - ) -} - -PureChoicesOption.PropTypes = { - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, -} - -function PureChoicesOption( { value, onChange, name, help, choices }) { - return ( - <label htmlFor=""> - { name } - <select name={name} onChange={onChange} title={help} selected={value}> - { choices.map((choice, index) => ( - <option key={index} value={choice}> {choice} </option> - ))} - </select> - </label> - ) -} - -const OptionTypes = { - bool: PureBooleanOption, - str: PureStringOption, - int: PureNumberOption, - "optional str": PureStringOption, - "sequence of str": PureStringOption, -} - -export default function OptionMaster({option, name, updateOptions, ...props}) { - let WrappedComponent = null - if (option.choices) { - WrappedComponent = PureChoicesOption - } else { - WrappedComponent = OptionTypes[option.type] - } - - let onChange = (e) => { - switch (option.type) { - case 'bool' : - updateOptions({[name]: !option.value}) - break - case 'int': - updateOptions({[name]: parseInt(e.target.value)}) - break - default: - updateOptions({[name]: e.target.value}) - } - } - return ( - <div className="menu-entry"> - <WrappedComponent - children={props.children} - value={option.value} - onChange={onChange} - name={name} - help={option.help} - choices={option.choices} - /> - </div> - ) -} diff --git a/web/src/js/components/Modal/OptionModal.jsx b/web/src/js/components/Modal/OptionModal.jsx index ef3a224a..5741ee8c 100644 --- a/web/src/js/components/Modal/OptionModal.jsx +++ b/web/src/js/components/Modal/OptionModal.jsx @@ -1,8 +1,22 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' -import * as modalAction from '../../ducks/ui/modal' -import { update as updateOptions } from '../../ducks/options' -import Option from './OptionMaster' +import React, { Component } from "react" +import { connect } from "react-redux" +import * as modalAction from "../../ducks/ui/modal" +import Option from "./Option" + +function PureOptionHelp({help}){ + return <div className="help-block small">{help}</div>; +} +const OptionHelp = connect((state, {name}) => ({ + help: state.options[name].help, +}))(PureOptionHelp); + +function PureOptionError({error}){ + if(!error) return null; + return <div className="small text-danger">{error}</div>; +} +const OptionError = connect((state, {name}) => ({ + error: state.ui.optionsEditor[name] && state.ui.optionsEditor[name].error +}))(PureOptionError); class PureOptionModal extends Component { @@ -28,23 +42,25 @@ class PureOptionModal extends Component { </div> <div className="modal-body"> - { - Object.keys(options).sort() - .map((key, index) => { - let option = options[key]; - return ( - <Option - key={index} - name={key} - updateOptions={updateOptions} - option={option} - />) - }) - } + <div className="form-horizontal"> + { + options.map(name => + <div key={name} className="form-group"> + <div className="col-xs-6"> + <label htmlFor={name}>{name}</label> + <OptionHelp name={name}/> + </div> + <div className="col-xs-6"> + <Option name={name}/> + <OptionError name={name}/> + </div> + </div> + ) + } + </div> </div> <div className="modal-footer"> - <button type="button" className="btn btn-primary">Save Changes</button> </div> </div> ) @@ -53,10 +69,9 @@ class PureOptionModal extends Component { export default connect( state => ({ - options: state.options + options: Object.keys(state.options).sort() }), { hideModal: modalAction.hideModal, - updateOptions: updateOptions, } )(PureOptionModal) |