@carbon/ibm-products
Version:
Carbon for IBM Products
250 lines (244 loc) • 9.44 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.
*/
import { extends as _extends } from '../../../_virtual/_rollupPluginBabelHelpers.js';
import React__default, { useContext, useRef, useState, useEffect, useCallback } from 'react';
import PropTypes from '../../../_virtual/index.js';
import { Button, Section, Heading } from '@carbon/react';
import { Add, TextNewLine } from '@carbon/react/icons';
import ConditionGroupBuilder from '../ConditionGroupBuilder/ConditionGroupBuilder.js';
import { ConditionBuilderContext, emptyState } from '../ConditionBuilderContext/ConditionBuilderProvider.js';
import { ConditionBuilderButton } from '../ConditionBuilderButton/ConditionBuilderButton.js';
import uuidv4 from '../../../global/js/utils/uuidv4.js';
import ConditionPreview from '../ConditionPreview/ConditionPreview.js';
import GroupConnector from '../ConditionBuilderConnector/GroupConnector.js';
import ConditionBuilderActions from '../ConditionBuilderActions/ConditionBuilderActions.js';
import { useTranslations } from '../utils/useTranslations.js';
import { blockClass, HIERARCHICAL_VARIANT } from '../utils/util.js';
var _GroupConnector;
const ConditionBuilderContent = _ref => {
let {
startConditionLabel,
getConditionState,
getActionsState,
initialState,
actions
} = _ref;
const {
rootState,
setRootState,
variant,
actionState,
onAddItem
} = useContext(ConditionBuilderContext);
const initialConditionState = useRef(initialState?.state ? JSON.parse(JSON.stringify(initialState?.state)) : null);
const [isConditionBuilderActive, setIsConditionBuilderActive] = useState(false);
const [showConditionGroupPreview, setShowConditionGroupPreview] = useState(false);
const [addConditionGroupText, conditionHeadingText, conditionBuilderHierarchicalText] = useTranslations(['addConditionGroupText', 'conditionHeadingText', 'conditionBuilderHierarchicalText']);
const showConditionGroupPreviewHandler = () => {
setShowConditionGroupPreview(true);
};
const hideConditionGroupPreviewHandler = () => {
setShowConditionGroupPreview(false);
};
useEffect(() => {
if (rootState?.groups?.length) {
setIsConditionBuilderActive(true);
} else {
setIsConditionBuilderActive(false);
}
if (getConditionState) {
getConditionState(rootState ?? {});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rootState]);
useEffect(() => {
getActionsState?.(actionState ?? []);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [actionState]);
useEffect(() => {
if (initialState?.enabledDefault) {
setRootState?.(initialState.state);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialState]);
const onStartConditionBuilder = () => {
//when add condition button is clicked.
setIsConditionBuilderActive(true);
if (initialConditionState?.current?.groups?.length) {
setRootState?.(initialConditionState.current);
initialConditionState.current = null;
} else {
setRootState?.(emptyState); //here we can set an empty skeleton object for an empty condition builder,
}
//or we can even pre-populate some existing builder and continue editing
};
const onRemove = useCallback(groupId => {
const groups = rootState?.groups?.filter(group => groupId !== group?.id);
setRootState?.({
...rootState,
groups: rootState ? groups : []
});
//set the initial state to empty.
if (groups?.length === 0) {
initialConditionState.current = null;
}
}, [setRootState, rootState]);
const onChangeHandler = (updatedGroup, groupIndex) => {
/**
* This method is triggered from inner components. This will be called every time when any change is to be updated in the rootState.
* This gets the updated group as argument.
*/
if (rootState && rootState.groups) {
const groups = [...(rootState.groups ? rootState.groups.slice(0, groupIndex) : []), updatedGroup, ...(rootState.groups ? rootState.groups.slice(groupIndex + 1) : [])];
setRootState?.({
...rootState,
groups
});
}
};
const addConditionGroupHandler = () => {
const {
preventAdd
} = onAddItem?.({
type: 'group',
state: rootState
}) ?? {};
if (!preventAdd) {
const newGroup = {
statement: 'ifAll',
groupOperator: 'and',
id: uuidv4(),
conditions: [{
property: undefined,
operator: '',
value: '',
popoverToOpen: 'propertyField',
id: uuidv4()
}]
};
setRootState?.({
...rootState,
groups: rootState && rootState.groups ? [...rootState.groups, newGroup] : [newGroup]
});
}
};
const getColorIndex = () => {
const groupLength = rootState?.groups?.length ?? 0;
return groupLength % 5;
};
if (!isConditionBuilderActive) {
return /*#__PURE__*/React__default.createElement(Button, {
className: `${blockClass}__addConditionText-button`,
renderIcon: props => /*#__PURE__*/React__default.createElement(Add, props),
iconDescription: startConditionLabel,
kind: "ghost",
size: "sm",
onClick: onStartConditionBuilder
}, startConditionLabel);
}
const wrapperRole = variant === HIERARCHICAL_VARIANT ? {
role: 'treegrid',
'aria-label': conditionBuilderHierarchicalText
} : null;
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Section, {
className: `${blockClass}__heading`,
level: 4
}, /*#__PURE__*/React__default.createElement(Heading, null, conditionHeadingText)), /*#__PURE__*/React__default.createElement("div", _extends({
className: `${blockClass}__content-container`
}, wrapperRole), rootState && rootState?.groups?.map((eachGroup, groupIndex) => /*#__PURE__*/React__default.createElement("div", {
key: eachGroup.id,
className: `${blockClass}__group-wrapper`
}, /*#__PURE__*/React__default.createElement(ConditionGroupBuilder, {
className: `${blockClass}__group`,
aria: {
level: 1,
posinset: groupIndex * 2 + 1,
setsize: (rootState.groups && rootState.groups.length * 2) ?? 0
},
group: eachGroup,
onRemove: () => {
onRemove(eachGroup.id);
},
onChange: updatedGroup => {
onChangeHandler(updatedGroup, groupIndex);
}
}), rootState.groups && groupIndex < rootState.groups.length - 1 && (_GroupConnector || (_GroupConnector = /*#__PURE__*/React__default.createElement(GroupConnector, null))))), variant == HIERARCHICAL_VARIANT && /*#__PURE__*/React__default.createElement("div", {
role: "row",
tabIndex: -1,
"aria-level": 1,
className: `${blockClass}__add-group`
}, /*#__PURE__*/React__default.createElement(ConditionBuilderButton, {
renderIcon: TextNewLine,
onClick: addConditionGroupHandler,
onMouseEnter: showConditionGroupPreviewHandler,
onMouseLeave: hideConditionGroupPreviewHandler,
onFocus: showConditionGroupPreviewHandler,
onBlur: hideConditionGroupPreviewHandler,
className: `${blockClass}__add-condition-group `,
hideLabel: true,
label: addConditionGroupText,
wrapperProps: {
role: 'gridcell',
'aria-label': addConditionGroupText
}
})), showConditionGroupPreview && /*#__PURE__*/React__default.createElement(ConditionPreview, {
previewType: "newGroup",
colorIndex: getColorIndex(),
group: {
groupOperator: rootState?.operator,
id: uuidv4()
}
})), actions && /*#__PURE__*/React__default.createElement(ConditionBuilderActions, {
actions: actions,
className: `${blockClass}__actions-container`
}));
};
ConditionBuilderContent.propTypes = {
/**
* optional array of object that give the list of actions.
*/
actions: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
label: PropTypes.string.isRequired
})),
/**
* callback functions that will provide the updated action state back.
*/
getActionsState: PropTypes.func,
/**
* This is a callback function that returns the updated state
*/
getConditionState: PropTypes.func.isRequired,
/**
* Optional prop if the condition building need to start from a predefined initial state
*/
initialState: PropTypes.shape({
state: PropTypes.shape({
groups: PropTypes.arrayOf(PropTypes.shape({
groupOperator: PropTypes.string,
statement: PropTypes.string,
conditions: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({
property: PropTypes.string,
operator: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
label: PropTypes.string
})), PropTypes.shape({
id: PropTypes.string,
label: PropTypes.string
})])
}), PropTypes.object]))
})),
operator: PropTypes.string
}),
enabledDefault: PropTypes.bool
}),
/* Provide a label to the button that starts condition builder
*/
startConditionLabel: PropTypes.string.isRequired
};
export { ConditionBuilderContent as default };