UNPKG

@carbon/ibm-products

Version:
347 lines (345 loc) 15.6 kB
/** * 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;