UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

236 lines (231 loc) 8.18 kB
/** * Copyright IBM Corp. 2020, 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { extends as _extends } from '../../../_virtual/_rollupPluginBabelHelpers.js'; import React__default, { useRef, useState, useContext, useEffect } from 'react'; import { Popover, PopoverContent, Layer, Section, Heading } from '@carbon/react'; import PropTypes from '../../../_virtual/index.js'; import { Add } from '@carbon/react/icons'; import { ConditionBuilderButton } from '../ConditionBuilderButton/ConditionBuilderButton.js'; import { useTranslations } from '../utils/useTranslations.js'; import { ConditionBuilderContext } from '../ConditionBuilderContext/ConditionBuilderProvider.js'; import { handleKeyDownForPopover } from '../utils/handleKeyboardEvents.js'; import { checkForMultiSelectOperator, blockClass, getValue } from '../utils/util.js'; import { translationsObject } from '../ConditionBuilderContext/translationObject.js'; const ConditionBuilderItem = _ref => { let { children, className, label, renderIcon, title, type, showToolTip, condition, popOverClassName, config, renderChildren, onChange, description, ...rest } = _ref; const popoverRef = useRef(null); const [open, setOpen] = useState(false); const { conditionBuilderRef, statementConfigCustom } = useContext(ConditionBuilderContext); const statementIdMap = { ifAll: translationsObject.ifText, ifAny: translationsObject.ifText, unlessAll: translationsObject.unlessText, unlessAny: translationsObject.unlessText }; //Appending statements from custom statement configuration if present statementConfigCustom?.forEach(statement => { statementIdMap[statement.id] = statement.label; }); const [invalidText, addConditionText, addPropertyText, addOperatorText, addValueText, labelText] = useTranslations(['invalidText', 'addConditionText', 'addPropertyText', 'addOperatorText', 'addValueText', label], statementIdMap); const getPropertyDetails = () => { const { property, operator } = condition || {}; if (label === 'INVALID' || rest['data-name'] === 'propertyField' && property === 'INVALID' || rest['data-name'] === 'operatorField' && operator === 'INVALID') { return { propertyLabel: invalidText, isInvalid: true }; } const propertyId = rest['data-name'] == 'valueField' && type ? getValue(type, label, config) : labelText; return { isInvalid: false, propertyLabel: propertyId }; }; const { propertyLabel, isInvalid } = getPropertyDetails(); useEffect(() => { /** * rest['data-name'] holds the current field name * popoverToOpen hold the next popover to be opened if required */ if (condition) { const currentField = rest['data-name']; //if any condition is changed, state prop is triggered if (condition.popoverToOpen && currentField !== condition.popoverToOpen) { // close the previous popover closePopover(); } else if (currentField == 'valueField' && type === 'option' && !checkForMultiSelectOperator(condition, config)) { //close the current popover if the field is valueField and is a single select dropdown. For all other inputs ,popover need to be open on value changes. closePopover(); } if (condition.popoverToOpen == currentField) { //current popover need to be opened openPopOver(); } } else { // when we change any statement(if/ excl.if) which is not part of condition state, label change is triggered. //close popOver when statement is changed. closePopover(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [condition, label]); useEffect(() => { //this will focus the first input field in the popover if (open && popoverRef.current) { const firstFocusableElement = popoverRef?.current?.querySelector('input,textarea'); if (firstFocusableElement) { setTimeout(() => firstFocusableElement.focus(), 0); } } }, [popoverRef, open]); const manageInvalidSelection = () => { //when the user didn't select any value , we need to show as incomplete if (rest['data-name'] === 'propertyField' && !condition?.property || rest['data-name'] === 'operatorField' && !condition?.operator || rest['data-name'] === 'valueField' && !condition?.value) { onChange?.('INVALID'); } }; const closePopover = () => { if (open) { manageInvalidSelection(); } setOpen(false); }; const openPopOver = () => setOpen(true); const togglePopover = () => { if (children || renderChildren) { setOpen(!open); } }; const handleKeyDownHandler = evt => { handleKeyDownForPopover(evt, conditionBuilderRef, popoverRef); if (evt.key === 'Escape') { manageInvalidSelection(); } }; const getCustomOperatorLabel = propertyLabel => { return propertyLabel && config?.operators?.find(operator => { return operator.id === propertyLabel; }); }; const getLabel = () => { if (config?.operators && rest['data-name'] === 'operatorField') { return getCustomOperatorLabel(propertyLabel)?.label ?? addOperatorText; } else if (propertyLabel) { return propertyLabel; } else if (rest['data-name'] === 'propertyField') { return addPropertyText; } else if (rest['data-name'] === 'operatorField') { return addOperatorText; } else if (rest['data-name'] === 'valueField') { return addValueText; } else { return addConditionText; } }; return /*#__PURE__*/React__default.createElement(Popover, { open: open, isTabTip: true, role: "gridcell", className: `${popOverClassName} ${blockClass}__popover`, ref: popoverRef, onRequestClose: closePopover }, /*#__PURE__*/React__default.createElement(ConditionBuilderButton, _extends({ label: getLabel(), hideLabel: !label ? true : false, onClick: togglePopover, className: className, "aria-haspopup": true, "aria-expanded": open, renderIcon: renderIcon ? renderIcon : label == undefined ? Add : undefined, showToolTip: showToolTip, isInvalid: isInvalid, description: description }, rest)), open && /*#__PURE__*/React__default.createElement(PopoverContent, { className: `${blockClass}__popover-content-wrapper`, role: "dialog", "aria-label": title, onKeyDown: handleKeyDownHandler }, /*#__PURE__*/React__default.createElement(Layer, null, /*#__PURE__*/React__default.createElement(Section, null, /*#__PURE__*/React__default.createElement(Heading, { className: `${blockClass}__item__title` }, title), /*#__PURE__*/React__default.createElement("div", { className: `${blockClass}__popover-content` }, renderChildren ? renderChildren(popoverRef) : children))))); }; ConditionBuilderItem.propTypes = { /** * provide the contents of the popover */ children: PropTypes.node, /** * Provide an optional class to be applied to the containing node. */ className: PropTypes.string, /** * current condition state object */ condition: PropTypes.object, /** * this is the config object again the current property from inputConfig */ config: PropTypes.object, /** * text to be displayed in the field */ label: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]), /** * callback to update the current condition of the state tree */ onChange: PropTypes.func, /** * class name for popover */ popOverClassName: PropTypes.string, /** * callback prop that returns the jsx for children */ renderChildren: PropTypes.func, /** * Optional prop to allow overriding the icon rendering. */ renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), /** * show tool tip */ showToolTip: PropTypes.bool, /** * title of the popover */ title: PropTypes.string, /** * input type */ type: PropTypes.string }; export { ConditionBuilderItem };