UNPKG

optimizely-oui

Version:

Optimizely's Component Library.

279 lines (249 loc) 8.52 kB
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;