Closed Xananax closed 8 years ago
Epic. Thanks for sharing!
Hi @Xananax, thanks for sharing.
Another option would be using a third party tagsinput (or build your own) and then a custom factory:
import React from 'react';
import t from 'tcomb-form';
import TagsInput from 'react-tagsinput'; // I'm using this but you can build your own (and reusable!) tagsinput
// import react-tagsinput.css...
class TagsComponent extends t.form.Component { // extend the base class
getTemplate() {
return (locals) => {
return (
<TagsInput value={locals.value} onChange={locals.onChange} />
);
};
}
}
export default TagsComponent;
@gcanti I realize this, but the problem with the React ecosystem currently is that you have a million components, and no idea what is maintained and what is not, what is compatible with 0.14 (react-dom split) and what is not...A million ways to handle styling (inline styles, bem-style classes, webpack-controller classnames, ...) or state, and so on...Which is why I'm more comfortable keeping control, specially for something as simple as this. I'd rather choose a minimal set of libraries that seems trustworthy and stick with them.
I also wanted to understand how factories work and I thought this would be a good exercise.
but the problem with the React ecosystem currently is that you have a million components... A million ways to handle styling ...
I feel your pain... :(
I also wanted to understand how factories work and I thought this would be a good exercise.
Just a note... yet another option is to extend t.form.Component
, it would:
getValidationOptions
, validate
shouldComponentUpdate
this.getLabel
, this.getName
, this.getError
in the render
method(or use the t.form.Textbox.prototype
as with getPlaceholder
)
Something like... (sorry for the different style / spaces but I have a draconian es-lint on)
//TagsComponent.js
import React, {Component, PropTypes} from 'react';
import t from 'tcomb-form';
import cx from 'classnames';
function Tag({tag, onClick}){
return <div>{tag} <span onClick={onClick}>x</span></div>;
}
class TagsComponent extends t.form.Component {
static propTypes = {
separator: PropTypes.instanceOf(RegExp)
, value: PropTypes.array
, currentValue: PropTypes.string
, component: PropTypes.element
}
static defaultProps = {
separator: /\s$/
, value: []
, currentValue: ''
}
constructor(props, context) {
super(props, context);
this.state = {
hasError: false
, value: props.value
, currentValue: props.currentValue
};
}
onChange(evt){
const currentValue = evt.target.value;
if(currentValue && this.props.separator.test(currentValue)){
const addedValue = currentValue.trim();
if(this.state.value && this.state.value.indexOf(addedValue) >= 0){
return this.setState({currentValue: ''});
}
const value = (this.state.value ? [...this.state.value, addedValue] : [addedValue]);
return this.setState(
{value, currentValue: ''}
, () => this.props.onChange(value, this.props.ctx.path)
);
}
this.setState({currentValue});
}
shouldComponentUpdate(nextProps, nextState) {
return super.shouldComponentUpdate(nextProps, nextState) || nextState.currentValue !== this.state.currentValue;
}
onRemoveTag(id, evt) {
if (evt) {
evt.preventDefault();
}
this.setState({value: this.state.value.filter((tag, i) => i !== id)});
}
renderTags(value){
return value.map((tag, id) =>
<Tag tag={tag} key={id} onClick={this.onRemoveTag.bind(this, id)}/>
);
}
render(){
var options = this.props.options || {};
const {value, currentValue, hasError} = this.state;
const {ctx} = this.props;
const CustomComponent = this.props.component;
const label = this.getLabel();
const placeholder = this.getPlaceholder();
const name = this.getName();
const error = this.getError();
var formGroupClasses = cx({
'form-group': true
, 'has-feedback': true
, 'has-error': hasError
});
const inputProps = {
disabled: options.disabled
, className: 'form-control'
, name
, placeholder
, onChange: this.onChange.bind(this)
, type: 'text'
, value: currentValue
};
const input = CustomComponent ? (<CustomComponent {...inputProps}/>) : (<input {...inputProps}/>);
return (<div className={formGroupClasses}>
{label ? (<label className="control-label">{label}</label>) : null}
{this.renderTags(value)}
{input}
<span className="glyphicon glyphicon-search form-control-feedback"></span>
{(error ? (<span className="help-block error-block">{error}</span>) : null)}
{(options.help ? (<span className="help-block">{options.help}</span>) : null)}
</div>);
}
}
TagsComponent.prototype.getPlaceholder = t.form.Textbox.prototype.getPlaceholder;
export default TagsComponent;
Since I had a bit of a hard time wrapping my head around custom factories, here's my tags input component, in the hopes it would be useful to someone else:
Use it like this:
Hope it helps anyone, comments would be welcome.