@carbon/ibm-products
Version:
Carbon for IBM Products
347 lines (345 loc) • 15.6 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.
*/
const require_runtime = require("../../../_virtual/_rolldown/runtime.js");
const require_index = require("../../../node_modules/classnames/index.js");
const require_uuidv4 = require("../../../global/js/utils/uuidv4.js");
const require_util = require("../utils/util.js");
const require_ConditionBuilderProvider = require("../ConditionBuilderContext/ConditionBuilderProvider.js");
const require_useTranslations = require("../utils/useTranslations.js");
const require_ConditionBuilderItem = require("../ConditionBuilderItem/ConditionBuilderItem.js");
const require_ItemOption = require("../ConditionBuilderItem/ConditionBuilderItemOption/ItemOption.js");
const require_useDataConfigs = require("../utils/useDataConfigs.js");
const require_ConditionConnector = require("../ConditionBuilderConnector/ConditionConnector.js");
const require_ConditionBlock = require("../ConditionBlock/ConditionBlock.js");
const require_ConditionPreview = require("../ConditionPreview/ConditionPreview.js");
let react = require("react");
react = require_runtime.__toESM(react);
let prop_types = require("prop-types");
prop_types = require_runtime.__toESM(prop_types);
//#region src/components/ConditionBuilder/ConditionGroupBuilder/ConditionGroupBuilder.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.
*/
var import_classnames = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const ConditionGroupBuilder = ({ group, aria, onRemove, onChange, className }) => {
const [conditionBuilderGroupText, conditionText, conditionBuilderText] = require_useTranslations.useTranslations([
"conditionBuilderGroupText",
"conditionText",
"conditionBuilderText"
]);
const { statementConfig } = require_useDataConfigs.useDataConfigs();
const { variant, conditionBuilderRef, statementConfigCustom, onRemoveItem, rootState } = (0, react.useContext)(require_ConditionBuilderProvider.ConditionBuilderContext);
const [showConditionPreview, setShowConditionPreview] = (0, react.useState)(-1);
const [showConditionSubGroupPreview, setShowConditionSubGroupPreview] = (0, react.useState)(-1);
(0, react.useState)(false);
const conditionBuilderContentRef = (0, react.useRef)(null);
const scrollParentRef = (0, react.useRef)(null);
(0, react.useEffect)(() => {
if (typeof conditionBuilderRef !== "function" && conditionBuilderRef?.current) scrollParentRef.current = getScrollableParent(conditionBuilderRef.current);
}, [conditionBuilderRef]);
(0, react.useEffect)(() => {
const parent = scrollParentRef.current;
let atBottom = false;
if (parent instanceof Window || parent === document.body || parent === document.documentElement) atBottom = window.innerHeight + window.scrollY >= document.body.scrollHeight;
else if (scrollParentRef.current) {
const parent = scrollParentRef.current;
atBottom = parent.scrollTop + parent.clientHeight >= parent.scrollHeight;
}
if ((showConditionPreview || showConditionSubGroupPreview) && atBottom) scrollParentRef.current?.scrollBy(0, -1);
}, [showConditionPreview, showConditionSubGroupPreview]);
const getScrollableParent = (element) => {
while (element && element !== document.body) {
const overflowY = getComputedStyle(element).overflowY;
if ((overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && element.scrollHeight > element.clientHeight) return element;
element = element.parentElement;
}
return window;
};
const onRemoveHandler = (conditionId, evt, conditionIndex) => {
const itemToRemove = group?.conditions?.find((condition) => condition.id === conditionId);
const removeType = itemToRemove?.conditions ? "subgroup" : "condition";
const { preventRemove } = onRemoveItem?.({
type: removeType,
state: rootState,
item: itemToRemove,
group
}) ?? {};
if (preventRemove) return;
if (group && group.conditions && group.conditions.length > 1) {
if (variant == "Hierarchical") handleFocusOnCloseHierarchical(evt);
else handleFocusOnClose(evt, conditionIndex);
if (!checkGroupHaveCondition(group.conditions, conditionId)) {
const allConditions = group?.conditions?.reduce((acc, condition) => {
const _condition = condition;
if (_condition.conditions) return acc.concat(_condition.conditions);
return acc;
}, []);
onChange?.({
...group,
conditions: allConditions
});
} else {
const filteredConditions = group.conditions.filter((condition) => conditionId !== condition.id);
if ((filteredConditions?.[0]).conditions) {
const firstFilteredConditions = filteredConditions[0];
filteredConditions.splice(0, 1, ...firstFilteredConditions?.conditions);
}
onChange?.({
...group,
conditions: filteredConditions
});
}
} else onRemove?.(evt);
};
const checkGroupHaveCondition = (conditions, conditionId) => {
return conditions.find((eachCondition) => {
return !eachCondition.conditions && eachCondition.id !== conditionId;
});
};
const onChangeHandler = (updatedCondition, conditionIndex) => {
const updatedConditions = [
...group?.conditions ? group.conditions.slice(0, conditionIndex) : [],
updatedCondition,
...group?.conditions ? group.conditions.slice(conditionIndex + 1) : []
];
onChange?.({
...group,
conditions: updatedConditions,
id: group?.id ?? require_uuidv4.default()
});
};
const addConditionHandler = (conditionIndex) => {
const newCondition = {
property: void 0,
operator: "",
value: "",
popoverToOpen: "propertyField",
id: require_uuidv4.default()
};
onChange?.({
...group,
conditions: [
...group.conditions ? group.conditions.slice(0, conditionIndex + 1) : [],
newCondition,
...group.conditions ? group.conditions.slice(conditionIndex + 1) : []
]
});
};
const handleFocusOnClose = (e, conditionIndex) => {
const currentGroupCloseButtons = e.currentTarget.closest(`.${require_util.blockClass}__group`)?.querySelectorAll("[data-name=\"closeCondition\"]");
if (conditionIndex == currentGroupCloseButtons.length - 1) require_util.manageTabIndexAndFocus(currentGroupCloseButtons[conditionIndex - 1], conditionBuilderRef);
else require_util.manageTabIndexAndFocus(currentGroupCloseButtons[conditionIndex + 1], conditionBuilderRef);
};
const handleFocusOnCloseHierarchical = (evt) => {
const currentLevel = evt.currentTarget?.closest("[role=\"row\"]")?.getAttribute("aria-level");
const currentPos = evt.currentTarget?.closest("[role=\"row\"]")?.getAttribute("aria-posinset");
const nextElement = conditionBuilderContentRef.current?.querySelector(`[aria-level="${currentLevel}"][aria-posinset="${Number(currentPos) + 1}"]`);
const prevElement = conditionBuilderContentRef.current?.querySelector(`[aria-level="${currentLevel}"][aria-posinset="${Number(currentPos) - 1}"]`);
if (nextElement?.classList.contains(`${require_util.blockClass}__condition-block`)) require_util.manageTabIndexAndFocus(nextElement?.querySelector("[data-name=\"closeCondition\"]"), conditionBuilderRef);
else if (prevElement?.classList.contains(`${require_util.blockClass}__condition-block`)) require_util.manageTabIndexAndFocus(prevElement?.querySelector("[data-name=\"closeCondition\"]"), conditionBuilderRef);
else {
const prevRows = conditionBuilderContentRef.current?.querySelectorAll(`[aria-level="${Number(currentLevel) - 1}"][role="row"]`);
const nextRow = conditionBuilderContentRef.current?.querySelector(`[aria-level="${Number(currentLevel) + 1}"][role="row"]`);
if (nextRow) {
const rowIdentity = {
ariaLevel: Number(nextRow.ariaLevel) - 1,
ariaPosInSet: nextRow.ariaPosInSet
};
setTimeout(() => {
const currentRowToFocus = conditionBuilderContentRef.current?.querySelector(`[role="row"][aria-level="${rowIdentity.ariaLevel}"][aria-posinset="${rowIdentity.ariaPosInSet}"]`);
require_util.manageTabIndexAndFocus(currentRowToFocus?.querySelector("[data-name=\"closeCondition\"]"), conditionBuilderRef);
}, 0);
} else if (prevRows?.length && prevRows.length > 1) require_util.manageTabIndexAndFocus(prevRows[prevRows.length - 2]?.querySelector("[data-name=\"closeCondition\"]"), conditionBuilderRef);
}
};
const addConditionSubGroupHandler = (conditionIndex) => {
const newSubGroup = require_ConditionBuilderProvider.getEmptyState(statementConfigCustom).groups?.[0];
onChange({
...group,
conditions: [
...group.conditions ? group.conditions.slice(0, conditionIndex + 1) : [],
newSubGroup,
...group.conditions ? group.conditions.slice(conditionIndex + 1) : []
]
});
};
const isLastCondition = (conditionIndex, conditionArr) => {
return conditionIndex + 1 >= conditionArr.length || conditionArr.length - 1 != conditionIndex && conditionArr[conditionIndex + 1].conditions;
};
const showConditionSubGroupPreviewHandler = (conditionIndex) => {
setShowConditionSubGroupPreview(conditionIndex);
};
const hideConditionSubGroupPreviewHandler = () => {
setShowConditionSubGroupPreview(-1);
};
const showConditionPreviewHandler = (conditionIndex) => {
setShowConditionPreview(conditionIndex);
};
const hideConditionPreviewHandler = () => {
setShowConditionPreview(-1);
};
const onStatementChangeHandler = (updatedStatement) => {
const groupOperator = statementConfig.find((statement) => statement.id == updatedStatement)?.connector;
onChange({
...group,
groupOperator,
statement: updatedStatement
});
};
const onConnectorOperatorChange = (op) => {
onChange({
...group,
groupOperator: op
});
};
const getNonHierarchicalVariant = () => {
return /* @__PURE__ */ react.default.createElement("div", { className: `${className} eachGroup` }, /* @__PURE__ */ react.default.createElement("div", {
className: `${require_util.blockClass}__condition-wrapper`,
role: "grid",
"aria-label": conditionBuilderText
}, group?.conditions?.map((eachCondition, conditionIndex) => /* @__PURE__ */ react.default.createElement("div", {
key: eachCondition.id,
className: `${require_util.blockClass}__group-wrapper`
}, /* @__PURE__ */ react.default.createElement(require_ConditionBlock.default, {
conjunction: conditionIndex > 0 ? group.groupOperator : void 0,
aria: {
level: aria.level + 1,
posinset: conditionIndex + 1,
setsize: group?.conditions?.length
},
isStatement: conditionIndex == 0,
condition: eachCondition,
group,
conditionIndex,
onChange: (updatedConditions) => {
onChangeHandler(updatedConditions, conditionIndex);
},
addConditionHandler,
onRemove: (e) => {
onRemoveHandler(eachCondition.id, e, conditionIndex);
},
onConnectorOperatorChange,
onStatementChange: onStatementChangeHandler,
isLastCondition
})))));
};
const getHierarchicalVariant = () => {
return /* @__PURE__ */ react.default.createElement("div", {
className: `${className} ${require_util.blockClass}__condition-wrapper`,
role: aria.level === 1 ? "rowgroup" : void 0,
"aria-label": aria.level == 1 ? conditionBuilderGroupText : void 0,
ref: conditionBuilderContentRef
}, /* @__PURE__ */ react.default.createElement("div", {
tabIndex: 0,
role: "row",
"aria-level": aria.level,
"aria-posinset": aria.posinset,
"aria-setsize": aria.setsize
}, /* @__PURE__ */ react.default.createElement(require_ConditionBuilderItem.ConditionBuilderItem, {
label: group.statement,
title: conditionText,
"data-name": "connectorField",
popOverClassName: `${require_util.blockClass}__gap ${require_util.blockClass}__connector`,
className: `${require_util.blockClass}__statement-button`
}, /* @__PURE__ */ react.default.createElement(require_ItemOption.ItemOption, {
conditionState: {
value: group.statement,
label: conditionText
},
onChange: (v, evt) => {
require_util.focusThisField(evt, conditionBuilderRef);
onStatementChangeHandler(v);
},
config: {
options: statementConfig,
isStatement: true
}
}))), group?.conditions?.map((eachCondition, conditionIndex) => /* @__PURE__ */ react.default.createElement(react.Fragment, { key: eachCondition.id }, eachCondition.conditions ? /* @__PURE__ */ react.default.createElement("div", { className: (0, import_classnames.default)(`${require_util.blockClass}__condition-block subgroup ${require_util.blockClass}__gap`, { [`${require_util.blockClass}__gap-bottom`]: group && group.conditions && group.conditions.length < conditionIndex + 1 }, {}) }, /* @__PURE__ */ react.default.createElement(require_ConditionConnector.default, {
className: `${require_util.blockClass}__gap ${require_util.blockClass}__gap-bottom ${require_util.blockClass}__groupConnector`,
operator: group.groupOperator,
"aria-hidden": true
}), /* @__PURE__ */ react.default.createElement(ConditionGroupBuilder, {
aria: {
level: aria.level + 1,
posinset: conditionIndex + 1,
setsize: group.conditions?.length ?? 0
},
className: `${require_util.blockClass}__group`,
group: eachCondition,
onChange: (updatedConditions) => {
onChangeHandler(updatedConditions, conditionIndex);
},
onRemove: (e) => {
onRemoveHandler(eachCondition.id, e, conditionIndex);
}
})) : /* @__PURE__ */ react.default.createElement("div", null, /* @__PURE__ */ react.default.createElement(require_ConditionBlock.default, {
conjunction: conditionIndex > 0 ? group.groupOperator : void 0,
aria: {
level: aria.level + 1,
posinset: conditionIndex + 1,
setsize: group.conditions?.length ?? 0
},
isStatement: false,
condition: eachCondition,
group,
conditionIndex,
addConditionHandler,
addConditionSubGroupHandler,
onChange: (updatedConditions) => {
onChangeHandler(updatedConditions, conditionIndex);
},
onRemove: (e) => {
onRemoveHandler(eachCondition.id, e, conditionIndex);
},
onConnectorOperatorChange,
onStatementChange: onStatementChangeHandler,
showConditionSubGroupPreviewHandler: () => {
showConditionSubGroupPreviewHandler(conditionIndex);
},
hideConditionSubGroupPreviewHandler,
showConditionPreviewHandler: () => {
showConditionPreviewHandler(conditionIndex);
},
hideConditionPreviewHandler,
isLastCondition
})), conditionIndex === showConditionSubGroupPreview && /* @__PURE__ */ react.default.createElement(require_ConditionPreview.default, {
previewType: "subGroup",
group
}), conditionIndex === showConditionPreview && /* @__PURE__ */ react.default.createElement(require_ConditionPreview.default, {
previewType: "condition",
group
}))));
};
return /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, variant == "Hierarchical" && getHierarchicalVariant(), variant == "Non-Hierarchical" && getNonHierarchicalVariant());
};
ConditionGroupBuilder.propTypes = {
/**
* object contains the aria attributes
*/
aria: prop_types.default.object,
/**
* state defines the current group
*/
/**
* Provide an optional class to be applied to the containing node.
*/
className: prop_types.default.string,
group: prop_types.default.object,
/**
* callback to update the current condition of the state tree
*/
onChange: prop_types.default.func,
/**
* call back to remove the particular group from the state tree
*/
onRemove: prop_types.default.func
};
//#endregion
exports.default = ConditionGroupBuilder;