@boomerang-io/carbon-addons-boomerang-react
Version:
Carbon Addons for Boomerang apps
149 lines (146 loc) • 9.08 kB
JavaScript
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 };