@carbon/ibm-products
Version:
Carbon for IBM Products
231 lines (229 loc) • 8.65 kB
JavaScript
/**
* Copyright IBM Corp. 2020, 2026
*
* 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 uuidv4 from "../../../global/js/utils/uuidv4.js";
import { blockClass } from "../utils/util.js";
import { ConditionBuilderContext, getEmptyState } from "../ConditionBuilderContext/ConditionBuilderProvider.js";
import { ConditionBuilderButton } from "../ConditionBuilderButton/ConditionBuilderButton.js";
import { useTranslations } from "../utils/useTranslations.js";
import ConditionPreview from "../ConditionPreview/ConditionPreview.js";
import ConditionGroupBuilder from "../ConditionGroupBuilder/ConditionGroupBuilder.js";
import GroupConnector from "../ConditionBuilderConnector/GroupConnector.js";
import ConditionBuilderActions from "../ConditionBuilderActions/ConditionBuilderActions.js";
import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import { Button, Heading, Section } from "@carbon/react";
import { Add, TextNewLine } from "@carbon/react/icons";
//#region src/components/ConditionBuilder/ConditionBuilderContent/ConditionBuilderContent.tsx
/**
* Copyright IBM Corp. 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
/**@ts-ignore */
const ConditionBuilderContent = ({ startConditionLabel, getConditionState, getActionsState, initialState, actions }) => {
const { rootState, setRootState, variant, actionState, onAddItem, onRemoveItem, readOnly, statementConfigCustom } = 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 ?? {});
}, [rootState]);
useEffect(() => {
getActionsState?.(actionState ?? []);
}, [actionState]);
useEffect(() => {
if (initialState?.enabledDefault) setRootState?.(initialState.state);
}, [initialState]);
const onStartConditionBuilder = () => {
setIsConditionBuilderActive(true);
if (initialConditionState?.current?.groups?.length) {
setRootState?.(initialConditionState.current);
initialConditionState.current = null;
} else setRootState?.(getEmptyState(statementConfigCustom));
};
const onRemove = useCallback((groupId) => {
const groupToRemove = rootState?.groups?.find((group) => group?.id === groupId);
const { preventRemove } = onRemoveItem?.({
type: "group",
state: rootState,
item: groupToRemove
}) ?? {};
if (!preventRemove) {
const groups = rootState?.groups?.filter((group) => groupId !== group?.id);
setRootState?.({
...rootState,
groups: rootState ? groups : []
});
if (groups?.length === 0) initialConditionState.current = null;
}
}, [
setRootState,
rootState,
onRemoveItem
]);
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 = getEmptyState(statementConfigCustom).groups?.[0];
setRootState?.({
...rootState,
groups: rootState && rootState.groups ? [...rootState.groups, newGroup] : [newGroup]
});
}
};
const getColorIndex = () => {
return (rootState?.groups?.length ?? 0) % 5;
};
if (!isConditionBuilderActive) return /* @__PURE__ */ React.createElement(Button, {
className: `${blockClass}__addConditionText-button`,
renderIcon: (props) => /* @__PURE__ */ React.createElement(Add, props),
iconDescription: startConditionLabel,
kind: "ghost",
size: "sm",
onClick: onStartConditionBuilder
}, startConditionLabel);
const wrapperRole = variant === "Hierarchical" ? {
role: "treegrid",
"aria-label": conditionBuilderHierarchicalText
} : null;
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Section, {
className: `${blockClass}__heading`,
level: 4
}, /* @__PURE__ */ React.createElement(Heading, null, conditionHeadingText)), /* @__PURE__ */ React.createElement("div", {
className: `${blockClass}__content-container`,
...wrapperRole
}, rootState && rootState?.groups?.map((eachGroup, groupIndex) => /* @__PURE__ */ React.createElement("div", {
key: eachGroup.id,
className: `${blockClass}__group-wrapper`
}, /* @__PURE__ */ React.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 && /* @__PURE__ */ React.createElement(GroupConnector, null))), !readOnly && variant == "Hierarchical" && /* @__PURE__ */ React.createElement("div", {
role: "row",
tabIndex: -1,
"aria-level": 1,
className: `${blockClass}__add-group`
}, /* @__PURE__ */ React.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
}
})), variant === "Hierarchical" ? /* @__PURE__ */ React.createElement(ConditionPreview, {
previewType: "newGroup",
colorIndex: getColorIndex(),
className: showConditionGroupPreview ? `${blockClass}__visible` : `${blockClass}__hidden`,
group: {
groupOperator: rootState?.operator,
id: uuidv4()
}
}) : null), actions && /* @__PURE__ */ React.createElement(ConditionBuilderActions, {
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
}),
startConditionLabel: PropTypes.string.isRequired
};
//#endregion
export { ConditionBuilderContent as default };