optimizely-oui
Version:
Optimizely's Component Library.
279 lines (249 loc) • 8.52 kB
JavaScript
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React from "react";
import PropTypes from "prop-types";
import ReactTagsInput from "react-tagsinput";
import Icon from "react-oui-icons";
import classNames from "classnames";
import Token from "../Token";
import { brandBlueDark } from "../../tokens/forimport/index.es";
/**
* @typedef {Object} TokenWrapper
* @property {String} name - The text display for the token.
* @property {String=} style - The style to use for displaying via <Token>
* Indicates how to style the token.
*/
/**
* Key codes which should be interpreted as an
* indication to add the current text as a new token.
*/
var ADD_KEYS = [
/** TAB */
{
keyCode: 9,
match: "\t"
},
/** ENTER */
{
keyCode: 13,
match: "\n"
},
/** COMMA */
{
keyCode: 188,
match: ","
}];
/**
* Tokens Input Component
* @param {Object} props - Properties passed to component
* @returns {ReactElement}
*/
export var TokensInput = function TokensInput(_ref) {
var addOnBlur = _ref.addOnBlur,
addOnPaste = _ref.addOnPaste,
className = _ref.className,
extraAddKeys = _ref.extraAddKeys,
hasSearchIcon = _ref.hasSearchIcon,
isDisabled = _ref.isDisabled,
maxTags = _ref.maxTags,
onChange = _ref.onChange,
onInputBlur = _ref.onInputBlur,
onInputChange = _ref.onInputChange,
onInputFocus = _ref.onInputFocus,
placeholder = _ref.placeholder,
tokens = _ref.tokens,
props = _objectWithoutProperties(_ref, ["addOnBlur", "addOnPaste", "className", "extraAddKeys", "hasSearchIcon", "isDisabled", "maxTags", "onChange", "onInputBlur", "onInputChange", "onInputFocus", "placeholder", "tokens"]);
/**
* Wrap the layout in a flexed <div>
* https://github.com/olahol/react-tagsinput#renderlayout
* @param {Array<ReactElement>} tokenComponents - Token components
* @param {ReactElement} inputComponent - Input component
* @returns {ReactElement}
*/
function renderLayout(tokenComponents, inputComponent) {
return React.createElement("div", {
className: "flex flex-1 flex-wrap flex-align--center",
style: {
resize: "both"
}
}, hasSearchIcon && React.createElement("span", {
className: "flex flex-align--center push-half--sides"
}, React.createElement(Icon, {
name: "search",
fill: brandBlueDark
})), tokenComponents, inputComponent);
}
/**
* Render an OUI Token to display each token.
* https://github.com/olahol/react-tagsinput#rendertag
*
* @param {Object} renderOptions - Values to render token
* @returns {ReactElement}
*/
// eslint-disable-next-line react/prop-types
function renderToken(_ref2) {
var tag = _ref2.tag,
key = _ref2.key,
onRemove = _ref2.onRemove;
function onDismiss() {
onRemove(key);
}
var name = tag.name,
style = tag.style,
backgroundColor = tag.backgroundColor;
return React.createElement(Token, {
key: key,
hasSnugWrap: true,
isDismissible: !isDisabled,
onDismiss: onDismiss,
name: name,
style: style,
backgroundColor: backgroundColor,
showWell: false,
testSection: "token-".concat(name)
});
}
/**
* Handler for when a string is pasted into the input.
* Splits the string on all the addKeys and extraAddKeys.
* https://github.com/olahol/react-tagsinput#pastesplit
*
* @param {string} str - The pasted string.
* @returns {Array<string>}
*/
function pasteSplit(str) {
return ADD_KEYS.map(function (k) {
return k.match;
}).concat(extraAddKeys) // Split the string by all our addKeys by joining with a
// \n, which we will use as the final split operator.
.reduce(function (acc, value) {
return acc.split(value).join("\n");
}, str).split("\n").filter(function (k) {
return !!k;
});
}
/**
* When the list of tokens changes, convert any string tokens
* to object form and ensure there are no duplicates.
* @param {Array.<TokenWrapper|String>} allTokens - All tokens
*/
function __onChange(allTokens) {
var updatedTokens = allTokens.reduce(function (acc, token) {
// A newly typed token will be in string form
if (typeof token === "string") {
token = {
name: token
};
}
if (!acc.find(function (item) {
return item.name === token.name;
})) {
acc.push(token);
}
return acc;
}, []).filter(function (token) {
return !!token.name;
});
onChange(updatedTokens);
}
var addKeys = ADD_KEYS.map(function (k) {
return k.keyCode;
}).concat(extraAddKeys);
var isNumberOfTokensMoreThanOrEqualToMaxTags = tokens.length >= maxTags && maxTags !== -1;
var minWidth = isNumberOfTokensMoreThanOrEqualToMaxTags ? "" : "min-width--150";
return React.createElement("div", _extends({
className: classNames("oui-text-input oui-pill-input text--left flush height--auto", className)
}, props), React.createElement(ReactTagsInput, {
addKeys: addKeys,
addOnBlur: addOnBlur,
addOnPaste: addOnPaste,
inputProps: {
className: "flex flex--1 ".concat(minWidth, " no-border soft-half--ends soft--sides border-radius push-half--left"),
onBlur: onInputBlur,
onChange: onInputChange,
onFocus: onInputFocus,
placeholder: isNumberOfTokensMoreThanOrEqualToMaxTags ? "" : placeholder,
readOnly: isNumberOfTokensMoreThanOrEqualToMaxTags,
disabled: isDisabled
},
maxTags: maxTags,
onChange: __onChange,
onlyUnique: true,
pasteSplit: pasteSplit,
renderTag: renderToken,
renderLayout: renderLayout,
value: tokens
}));
};
TokensInput.propTypes = {
/**
* Adds a new tag on input blur
*/
addOnBlur: PropTypes.bool,
/**
* Adds a new tag on input paste
*/
addOnPaste: PropTypes.bool,
/** CSS class names. */
className: PropTypes.string,
/**
* Additional keycodes which should be considered
* an intent to enter the current string as a new Token.
* See ADD_KEYS above.
*/
extraAddKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
/**
* Whether a search icon should be present
*/
hasSearchIcon: PropTypes.bool,
/**
* Whether the input is disabled or not
*/
isDisabled: PropTypes.bool,
/**
* Maximum number of allowed tokens (pass-through to <ReactTagsInput>)
*/
maxTags: PropTypes.number,
/**
* Handler to invoke when the token list changes.
*/
onChange: PropTypes.func.isRequired,
/**
* Handler to invoke when the token input is blurred
*/
onInputBlur: PropTypes.func,
/**
* Handler to invoke when the token input changes
*/
onInputChange: PropTypes.func,
/**
* Handler to invoke when the token input is focused
*/
onInputFocus: PropTypes.func,
/**
* Placeholder text for the input box.
*/
placeholder: PropTypes.string,
/**
* @type {Array.<TokenWrapper>}
*/
tokens: PropTypes.arrayOf(PropTypes.object).isRequired
};
TokensInput.defaultProps = {
addOnBlur: true,
addOnPaste: true,
extraAddKeys: [],
maxTags: -1,
onInputBlur: function onInputBlur() {
return null;
},
onInputChange: function onInputChange() {
return null;
},
onInputFocus: function onInputFocus() {
return null;
},
placeholder: "enter tokens"
};
export default TokensInput;