UNPKG

project-nexus

Version:

A hub for all your programming projects

372 lines (321 loc) 9.46 kB
(function(){ var React = this.React || require("react"); var joinClasses = (function() { var slice = [].slice; var args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return ((function() { var a, i, len, results; results = []; for (i = 0, len = args.length; i < len; i++) { a = args[i]; if (a) { results.push(a.split(" ")); } } return results; })()).join(" "); }); var KEY_CODES = { ENTER: 13, BACKSPACE: 8 }; var DefaultTagComponent = React.createClass({displayName: "DefaultTagComponent", render: function () { var self = this, p = self.props; return ( React.createElement("div", {className: joinClasses("tag", p.classes)}, React.createElement("div", {className: "tag-text", onClick: p.onEdit}, p.item), React.createElement("div", {className: "remove", onClick: p.onRemove}, p.removeTagLabel ) ) ); } }); var TaggedInput = React.createClass({displayName: "TaggedInput", propTypes: { onBeforeAddTag: React.PropTypes.func, onAddTag: React.PropTypes.func, onBeforeRemoveTag: React.PropTypes.func, onRemoveTag: React.PropTypes.func, onEnter: React.PropTypes.func, onBlur: React.PropTypes.func, onFocus: React.PropTypes.func, unique: React.PropTypes.oneOfType([React.PropTypes.bool, React.PropTypes.func]), autofocus: React.PropTypes.bool, backspaceDeletesWord: React.PropTypes.bool, placeholder: React.PropTypes.string, tags: React.PropTypes.arrayOf(React.PropTypes.any), removeTagLabel: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]), delimiters: React.PropTypes.arrayOf(function (props, propName, componentName) { if (typeof props[propName] !== 'string' || props[propName].length !== 1) { return new Error('TaggedInput prop delimiters must be an array of 1 character strings') } }), tagOnBlur: React.PropTypes.bool, tabIndex: React.PropTypes.number, clickTagToEdit: React.PropTypes.bool }, getDefaultProps: function () { return { delimiters: [' ', ','], unique: true, autofocus: false, backspaceDeletesWord: true, tagOnBlur: false, clickTagToEdit: false, onBeforeAddTag: function (tag) { return true; }, onBeforeRemoveTag: function (index) { return true; } }; }, getInitialState: function () { return { tags: (this.props.tags || []).slice(0), currentInput: null }; }, render: function () { var self = this, s = self.state, p = self.props; var tagComponents = [], classes = "tagged-input-wrapper", placeholder, i; if (p.classes) { classes += ' ' + p.classes; } if (s.tags.length === 0) { placeholder = p.placeholder; } var TagComponent = DefaultTagComponent; for (i = 0; i < s.tags.length; i++) { tagComponents.push( React.createElement(TagComponent, { key: 'tag' + i, item: s.tags[i], itemIndex: i, onRemove: self._handleRemoveTag.bind(this, i), onEdit: p.clickTagToEdit ? self._handleEditTag.bind(this, i) : null, classes: p.unique && (i === s.duplicateIndex) ? 'duplicate' : '', removeTagLabel: p.removeTagLabel || "\u274C"} ) ); } var input = ( React.createElement("input", {type: "text", className: "tagged-input", ref: "input", onKeyUp: this._handleKeyUp, onKeyDown: this._handleKeyDown, onChange: this._handleChange, onBlur: this._handleBlur, onFocus: this._handleFocus, value: s.currentInput, placeholder: placeholder, tabIndex: p.tabIndex} ) ); return ( React.createElement("div", {className: classes, onClick: self._handleClickOnWrapper}, tagComponents, input ) ); }, componentDidMount: function () { var self = this, s = self.state, p = self.props; if (p.autofocus) { self.refs.input.getDOMNode().focus(); } }, componentWillReceiveProps: function (nextProps) { this.setState({ tags: nextProps.tags }) }, _handleRemoveTag: function (index) { var self = this, s = self.state, p = self.props; if (p.onBeforeRemoveTag(index)) { var removedItems = s.tags.splice(index, 1); if (s.duplicateIndex) { self.setState({duplicateIndex: null}, function () { if (p.onRemoveTag) { p.onRemoveTag(removedItems[0]); } }); } else { if (p.onRemoveTag) { p.onRemoveTag(removedItems[0]); } self.forceUpdate(); } } }, _handleEditTag: function (index) { var self = this, s = self.state, p = self.props; if (s.currentInput) { var trimmedInput = s.currentInput.trim(); if (trimmedInput && (this.state.tags.indexOf(trimmedInput) < 0 || !p.unique)) { this._validateAndTag(s.currentInput); } } var removedItems = s.tags.splice(index, 1); if (s.duplicateIndex) { self.setState({duplicateIndex: null, currentInput: removedItems[0]}, function () { if (p.onRemoveTag) { p.onRemoveTag(removedItems[0]); } }); } else { self.setState({currentInput: removedItems[0]}, function () { if (p.onRemoveTag) { p.onRemoveTag(removedItems[0]); } }); } }, _handleKeyUp: function (e) { var self = this, s = self.state, p = self.props; var enteredValue = e.target.value; switch (e.keyCode) { case KEY_CODES.ENTER: if (s.currentInput) { self._validateAndTag(s.currentInput, function (status) { if (p.onEnter) { p.onEnter(e, s.tags); } }); } break; } }, _handleKeyDown: function (e) { var self = this, s = self.state, p = self.props, poppedValue, newCurrentInput; switch (e.keyCode) { case KEY_CODES.BACKSPACE: if (!e.target.value || e.target.value.length < 0) { if (p.onBeforeRemoveTag(s.tags.length - 1)) { poppedValue = s.tags.pop(); newCurrentInput = p.backspaceDeletesWord ? '' : poppedValue; this.setState({ currentInput: newCurrentInput, duplicateIndex: null }); if (p.onRemoveTag && poppedValue) { p.onRemoveTag(poppedValue); } } } break; } }, _handleChange: function (e) { var self = this, s = self.state, p = self.props; var value = e.target.value, lastChar = value.charAt(value.length - 1), tagText = value.substring(0, value.length - 1); if (p.delimiters.indexOf(lastChar) !== -1) { self._validateAndTag(tagText); } else { this.setState({ currentInput: e.target.value }); } }, _handleBlur: function (e) { if (this.props.tagOnBlur) { var value = e.target.value; if (value) { this._validateAndTag(value) } } if (this.props.onBlur) { this.props.onBlur(e); } }, _handleFocus: function (e) { if (this.props.onFocus) { this.props.onFocus(e); } }, _handleClickOnWrapper: function (e) { this.refs.input.getDOMNode().focus(); }, _validateAndTag: function (tagText, callback) { var self = this, s = self.state, p = self.props; var duplicateIndex; var trimmedText; if (tagText && tagText.length > 0) { trimmedText = tagText.trim(); if (p.unique) { // not a boolean, it's a function if (typeof p.unique === 'function') { duplicateIndex = p.unique(this.state.tags, trimmedText); } else { duplicateIndex = this.state.tags.indexOf(trimmedText); } if (duplicateIndex === -1) { if (p.onBeforeAddTag(trimmedText)) { s.tags.push(trimmedText); } self.setState({ currentInput: '', duplicateIndex: null }, function () { if (p.onAddTag) { p.onAddTag(tagText); } if (callback) { callback(true); } }); } else { self.setState({duplicateIndex: duplicateIndex}, function () { if (callback) { callback(false); } }); } } else { if (p.onBeforeAddTag(trimmedText)) { s.tags.push(trimmedText); } self.setState({currentInput: ''}, function () { if (p.onAddTag) { p.onAddTag(tagText); } if (callback) { callback(true); } }); } } }, getTags: function () { return this.state.tags; }, getEnteredText: function () { return this.state.currentInput; }, getAllValues: function () { var self = this, s = this.state, p = this.props; if (s.currentInput && s.currentInput.length > 0) { return this.state.tags.concat(s.currentInput); } else { return this.state.tags; } } }); if ((typeof module !== "undefined" && module !== null ? module.exports : void 0) != null) { module.exports = TaggedInput; } else { this.TaggedInput = TaggedInput; } }());