Use Preact for settings and show validation

jh-changes
Shin'ya Ueoka 7 years ago
parent 44459e39c3
commit d33b37cdb9
  1. 48
      src/settings/components/index.jsx
  2. 2
      src/settings/components/site.scss
  3. 50
      src/settings/components/ui/input.jsx
  4. 17
      src/settings/components/ui/input.scss

@ -1,5 +1,6 @@
import './site.scss'; import './site.scss';
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import Input from './ui/input';
import * as settingActions from 'settings/actions/setting'; import * as settingActions from 'settings/actions/setting';
import * as validator from 'shared/validators/setting'; import * as validator from 'shared/validators/setting';
@ -10,6 +11,9 @@ class SettingsComponent extends Component {
this.state = { this.state = {
settings: { settings: {
json: '', json: '',
},
errors: {
json: '',
} }
}; };
this.context.store.subscribe(this.stateChanged.bind(this)); this.context.store.subscribe(this.stateChanged.bind(this));
@ -35,39 +39,47 @@ class SettingsComponent extends Component {
<h1>Configure Vim-Vixen</h1> <h1>Configure Vim-Vixen</h1>
<form className='vimvixen-settings-form'> <form className='vimvixen-settings-form'>
<p>Load settings from:</p> <Input
<input type='radio' id='setting-source-json' type='radio'
name='source' name='source'
value='json' label='Use plain JSON'
onChange={this.bindAndSave.bind(this)} checked={this.state.settings.source === 'json'}
checked={this.state.settings.source === 'json'} /> value='json' />
<label htmlFor='settings-source-json'>JSON</label>
<textarea name='json' spellCheck='false' <Input
onInput={this.validate.bind(this)} type='textarea'
name='json'
label='Plane JSON'
spellCheck='false'
error={this.state.errors.json}
onChange={this.bindValue.bind(this)} onChange={this.bindValue.bind(this)}
onBlur={this.bindAndSave.bind(this)} onBlur={this.bindAndSave.bind(this)}
value={this.state.settings.json} /> value={this.state.settings.json}
/>
</form> </form>
</div> </div>
); );
} }
validate(e) { validate(target) {
try { if (target.name === 'json') {
let settings = JSON.parse(e.target.value); let settings = JSON.parse(target.value);
validator.validate(settings); validator.validate(settings);
e.target.setCustomValidity('');
} catch (err) {
e.target.setCustomValidity(err.message);
} }
} }
bindValue(e) { bindValue(e) {
let nextSettings = Object.assign({}, this.state.settings); let next = Object.assign({}, this.state);
nextSettings[e.target.name] = e.target.value;
next.errors.json = '';
try {
this.validate(e.target);
} catch (err) {
next.errors.json = err.message;
}
next.settings[e.target.name] = e.target.value;
this.setState({ settings: nextSettings }); this.setState(next);
} }
bindAndSave(e) { bindAndSave(e) {

@ -1,4 +1,6 @@
.vimvixen-settings-form { .vimvixen-settings-form {
padding: 2px;
textarea[name=json] { textarea[name=json] {
font-family: monospace; font-family: monospace;
width: 100%; width: 100%;

@ -0,0 +1,50 @@
import { h, Component } from 'preact';
import './input.scss';
class Input extends Component {
renderRadio(props) {
let inputClasses = 'form-field-input';
if (props.error) {
inputClasses += ' input-error';
}
return <div>
<label>
<input className={ inputClasses } type='radio' {...props} />
{ props.label }
</label>
</div>;
}
renderTextArea(props) {
let inputClasses = 'form-field-input';
if (props.error) {
inputClasses += ' input-error';
}
return <div>
<label
className='form-field-label'
htmlFor={props.id}
>{ props.label }</label>
<textarea className={inputClasses} {...props} />
<p className='form-field-error'>{ this.props.error }</p>
</div>;
}
render() {
let { type } = this.props;
switch (this.props.type) {
case 'radio':
return this.renderRadio(this.props);
case 'textarea':
return this.renderTextArea(this.props);
default:
console.warn(`Unsupported input type ${type}`);
}
return null;
}
}
export default Input;

@ -0,0 +1,17 @@
.form-field-label {
font-weight: bold
}
.form-field-error {
font-weight: bold;
color: red;
min-height: 1.5em;
}
.form-field-input {
padding: 4px;
}
.form-field-input.input-error {
box-shadow: 0 0 2px red;
}