@carbon/ibm-products
Version:
Carbon for IBM Products
238 lines (232 loc) • 8.36 kB
JavaScript
/**
* 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.
*/
'use strict';
var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
var React = require('react');
var react = require('@carbon/react');
var index = require('../../../_virtual/index.js');
var icons = require('@carbon/react/icons');
var ConditionBuilderButton = require('../ConditionBuilderButton/ConditionBuilderButton.js');
var useTranslations = require('../utils/useTranslations.js');
var ConditionBuilderProvider = require('../ConditionBuilderContext/ConditionBuilderProvider.js');
var handleKeyboardEvents = require('../utils/handleKeyboardEvents.js');
var util = require('../utils/util.js');
var translationObject = require('../ConditionBuilderContext/translationObject.js');
const ConditionBuilderItem = _ref => {
let {
children,
className,
label,
renderIcon,
title,
type,
showToolTip,
condition,
popOverClassName,
config,
renderChildren,
onChange,
description,
...rest
} = _ref;
const popoverRef = React.useRef(null);
const [open, setOpen] = React.useState(false);
const {
conditionBuilderRef,
statementConfigCustom
} = React.useContext(ConditionBuilderProvider.ConditionBuilderContext);
const statementIdMap = {
ifAll: translationObject.translationsObject.ifText,
ifAny: translationObject.translationsObject.ifText,
unlessAll: translationObject.translationsObject.unlessText,
unlessAny: translationObject.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.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 ? util.getValue(type, label, config) : labelText;
return {
isInvalid: false,
propertyLabel: propertyId
};
};
const {
propertyLabel,
isInvalid
} = getPropertyDetails();
React.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' && !util.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]);
React.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 => {
handleKeyboardEvents.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.createElement(react.Popover, {
open: open,
isTabTip: true,
role: "gridcell",
className: `${popOverClassName} ${util.blockClass}__popover`,
ref: popoverRef,
onRequestClose: closePopover
}, /*#__PURE__*/React.createElement(ConditionBuilderButton.ConditionBuilderButton, _rollupPluginBabelHelpers.extends({
label: getLabel(),
hideLabel: !label ? true : false,
onClick: togglePopover,
className: className,
"aria-haspopup": true,
"aria-expanded": open,
renderIcon: renderIcon ? renderIcon : label == undefined ? icons.Add : undefined,
showToolTip: showToolTip,
isInvalid: isInvalid,
description: description
}, rest)), open && /*#__PURE__*/React.createElement(react.PopoverContent, {
className: `${util.blockClass}__popover-content-wrapper`,
role: "dialog",
"aria-label": title,
onKeyDown: handleKeyDownHandler
}, /*#__PURE__*/React.createElement(react.Layer, null, /*#__PURE__*/React.createElement(react.Section, null, /*#__PURE__*/React.createElement(react.Heading, {
className: `${util.blockClass}__item__title`
}, title), /*#__PURE__*/React.createElement("div", {
className: `${util.blockClass}__popover-content`
}, renderChildren ? renderChildren(popoverRef) : children)))));
};
ConditionBuilderItem.propTypes = {
/**
* provide the contents of the popover
*/
children: index.default.node,
/**
* Provide an optional class to be applied to the containing node.
*/
className: index.default.string,
/**
* current condition state object
*/
condition: index.default.object,
/**
* this is the config object again the current property from inputConfig
*/
config: index.default.object,
/**
* text to be displayed in the field
*/
label: index.default.oneOfType([index.default.string, index.default.array, index.default.object]),
/**
* callback to update the current condition of the state tree
*/
onChange: index.default.func,
/**
* class name for popover
*/
popOverClassName: index.default.string,
/**
* callback prop that returns the jsx for children
*/
renderChildren: index.default.func,
/**
* Optional prop to allow overriding the icon rendering.
*/
renderIcon: index.default.oneOfType([index.default.func, index.default.object]),
/**
* show tool tip
*/
showToolTip: index.default.bool,
/**
* title of the popover
*/
title: index.default.string,
/**
* input type
*/
type: index.default.string
};
exports.ConditionBuilderItem = ConditionBuilderItem;