UNPKG

@boomerang-io/carbon-addons-boomerang-react

Version:
149 lines (146 loc) 9.08 kB
import React, { useState } from 'react'; import { TextInput, Button, Tag } from '@carbon/react'; import { Information, Add } from '@carbon/react/icons'; import cx from 'classnames'; import TooltipHover from '../TooltipHover/TooltipHover.js'; import { isAccessibleKeyDownEvent } from '../../tools/accessibility.js'; import { prefix } from '../../internal/settings.js'; /* IBM Confidential 694970X, 69497O0 © Copyright IBM Corp. 2022, 2024 */ function CreatableComponent({ buttonClassName = `${prefix}--bmrg-creatable__button`, buttonContent = "Add", buttonProps, createKeyValuePair = false, disabled, id, initialValues: externalInitialValues, invalid, invalidText, helperText, key, keyHelperText, keyLabel, keyLabelText, keyPlaceholder, label, labelText = "", max, nonDeletable = false, onKeyBlur, onValueBlur, onInputBlur, onChange, placeholder, tagProps, tagType = "teal", textInputProps, tooltipClassName = `${prefix}--bmrg-creatable__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, type = "text", valueHelperText, valueLabel, valueLabelText, valuePlaceholder, value: externalValues, values, trimInput = false, removeOnlyFirst = false, }) { const [keyValue, setKeyValue] = useState(""); const [value, setValue] = useState(""); const [input, setInput] = useState(""); const inputRef = React.useRef(null); const inputLabel = labelText || label; const inputKeyLabel = keyLabelText || keyLabel; const inputValueLabel = valueLabelText || valueLabel; const [createdItems, setCreatedItems] = useState([]); const createButtonClassName = cx(buttonClassName, { "--no-label": (!createKeyValuePair && !inputLabel && !tooltipContent) || (createKeyValuePair && !inputKeyLabel && !inputValueLabel), }); /** Add support for csv strings */ let finalValues; let finalExternalValues; let finalExternalInitialValues; if (typeof values === "string") { finalValues = values === "" ? [] : values.split(","); } else { finalValues = values; } if (typeof externalValues === "string") { finalExternalValues = externalValues === "" ? [] : externalValues.split(","); } else { finalExternalValues = externalValues; } if (typeof externalInitialValues === "string") { finalExternalInitialValues = externalInitialValues.split(","); } else { finalExternalInitialValues = externalInitialValues; } const [initialItems] = useState(finalValues || finalExternalValues ? finalValues || finalExternalValues : []); const tagItems = (finalValues || finalExternalValues ? finalValues || finalExternalValues : createdItems); // Externally controlled if values props exists const initialTagItems = finalExternalInitialValues || initialItems || []; // Externally controlled if initialValues props exists const existValue = (keyValue && value) || (trimInput ? input?.trim() : input); const hasBothHelperText = keyHelperText && valueHelperText; const hasBothLabelText = inputKeyLabel && inputValueLabel; const tagsToShow = finalExternalInitialValues ? [...initialTagItems, ...tagItems] : [...tagItems]; const disableInputs = disabled || (max && tagsToShow.length >= max && max > 0); // Check if key value pair has colon const keyInputHasColon = keyValue?.includes(":"); const valueInputHasColon = value?.includes(":"); const isKeyInputValid = invalid || keyInputHasColon; const isValueInputValid = invalid || valueInputHasColon; const keyInputInvalidText = keyInputHasColon ? '":" is not allowed' : invalidText; const valueInputInvalidText = valueInputHasColon ? '":" is not allowed' : invalidText; const isAddButtonDisabled = disabled || !existValue || keyInputHasColon || valueInputHasColon; const onInputChange = (e) => { setInput(e.target.value); }; const onKeyChange = (e) => { setKeyValue(e.target.value); }; const onValueChange = (e) => { setValue(e.target.value); }; const addValue = (e) => { e.preventDefault(); const items = [...tagItems]; if (createKeyValuePair && keyValue && value) { items.push(`${keyValue}:${value}`); setKeyValue(""); setValue(""); } else { if (input) { // Check if input is a JSON object const inputToTest = input.trim(); if (inputToTest.slice(0, 1) === "{" && inputToTest.slice(-1) === "}") { const inputJsonObject = JSON.parse(input); if (typeof inputJsonObject === "object" && inputJsonObject !== null) { for (const [key, values] of Object.entries(inputJsonObject)) { const valuesArray = [...values]; valuesArray.forEach((value) => items.push(`${key}:${value}`)); } } } else { items.push(input); } } setInput(""); } setCreatedItems(items); if (onChange) onChange(items); inputRef.current?.focus(); }; const removeValue = (value) => { let items; if (removeOnlyFirst) { items = [...tagItems]; const index = items.indexOf(value); if (index !== -1) { items.splice(index, 1); } } else { items = tagItems.filter((item) => item !== value); } setCreatedItems(items); if (onChange) onChange(items); }; return (React.createElement("div", { key: key, className: `${prefix}--bmrg-creatable` }, React.createElement("div", { className: `${prefix}--bmrg-creatable__input` }, createKeyValuePair ? (React.createElement("div", { className: `${prefix}--bmrg-creatable__key-value-inputs` }, React.createElement("div", { style: { marginBottom: hasBothHelperText || keyHelperText ? "0" : valueHelperText ? "1.5rem" : "0rem", marginTop: hasBothLabelText || inputKeyLabel ? "0" : inputValueLabel ? "1.5rem" : "0.5rem", } }, React.createElement(TextInput, { disabled: disableInputs, id: `${id}-key`, invalid: isKeyInputValid, invalidText: keyInputInvalidText, helperText: keyHelperText, labelText: inputKeyLabel, onBlur: onKeyBlur, onChange: onKeyChange, placeholder: keyPlaceholder, ref: inputRef, type: type, value: keyValue, ...textInputProps })), React.createElement("span", { className: `${prefix}--bmrg-creatable__colon`, style: { marginTop: inputKeyLabel || inputValueLabel ? "2.25rem" : "1.25rem", } }, ":"), React.createElement("div", { style: { marginBottom: hasBothHelperText || valueHelperText ? "0" : keyHelperText ? "1.5rem" : "0rem", marginTop: hasBothLabelText || inputValueLabel ? "0" : inputKeyLabel ? "1.5rem" : "0.5rem", } }, React.createElement(TextInput, { disabled: disableInputs, id: `${id}-value`, invalid: isValueInputValid, invalidText: valueInputInvalidText, helperText: valueHelperText, labelText: inputValueLabel, onBlur: onValueBlur, onChange: onValueChange, placeholder: valuePlaceholder, type: type, value: value, ...textInputProps })))) : (React.createElement(TextInput, { disabled: disableInputs, id: id, invalid: invalid, invalidText: invalidText, helperText: helperText, labelText: React.createElement("div", { style: { display: "flex" } }, React.createElement("div", null, inputLabel), tooltipContent && (React.createElement("div", { className: tooltipClassName }, React.createElement(TooltipHover, { ...tooltipProps, tooltipText: tooltipContent }, React.createElement(Information, { size: 16, fill: "currentColor" }))))), onBlur: onInputBlur, onChange: onInputChange, placeholder: placeholder, ref: inputRef, type: type, value: input, ...textInputProps })), React.createElement(Button, { className: createButtonClassName, disabled: isAddButtonDisabled, onClick: addValue, iconDescription: "Add", renderIcon: Add, size: "md", type: "button", ...buttonProps }, buttonContent)), React.createElement("div", { className: `${prefix}--bmrg-creatable__tags` }, tagsToShow.map((item, index) => (React.createElement(Tag, { key: `${item}-${index}`, disabled: disabled, type: tagType, onClick: nonDeletable && initialTagItems.includes(item) ? undefined : () => removeValue(item), onKeyDown: nonDeletable && initialTagItems.includes(item) ? undefined : (e) => isAccessibleKeyDownEvent(e) && removeValue(item), filter: !nonDeletable || (nonDeletable && !initialTagItems.includes(item)), ...tagProps }, item)))))); } export { CreatableComponent as default };