import { getParent, hasParent, types } from "mobx-state-tree"; import { FF_LSDV_4583, isFF } from "../utils/feature-flags"; /** * Looks for a given visibility parameter in the node or its parents. * For convenience users can specify visibility parameters in parent `View` tag up the tree. * @todo We should use it for all params but for now it's only used for `whenrole`. * @param {Object} node Control tag * @param {string} param Visibility parameter name (whenrole, whenlabelvalue, whenchoicevalue) * @returns {string|null} */ function findVisibilityParam(node, param) { while (node) { if (node[param]) { return node[param]; } // all tags are sitting in `children` array of their parent, // so we need to go up 2 levels to get the parent if (!hasParent(node, 2)) break; node = getParent(node, 2); } return null; } const RequiredMixin = types .model({ required: types.optional(types.boolean, false), requiredmessage: types.maybeNull(types.string), }) .actions((self) => { const Super = { validate: self.validate, }; return { validate() { if (!Super.validate()) return false; if (!self.required) return true; if (self.perregion) { // validating when choices labeling is done per region, // for example choice may be required to be selected for // every bbox const objectTag = self.toNameTag; // if regions don't meet visibility conditions skip validation for (const reg of objectTag.allRegs) { const s = reg.results.find((s) => s.from_name === self); if (self.visiblewhen === "region-selected") { if (self.whentagname) { const label = reg.labeling?.from_name?.name; if (label && label !== self.whentagname) continue; } } if (reg.role) { const whenRoles = findVisibilityParam(self, "whenrole")?.split(",") ?? null; if (whenRoles && !whenRoles.includes(reg.role)) continue; } if (self.whenlabelvalue && !reg.hasLabel(self.whenlabelvalue)) { continue; } if (!s?.hasValue) { self.annotation.selectArea(reg); self.requiredModal(); return false; } } } else if (isFF(FF_LSDV_4583) && self.peritem) { // validating when choices labeling is done per item, const objectTag = self.toNameTag; const maxItemIndex = objectTag.maxItemIndex; const existingResultsIndexes = self.annotation.regions.reduce((existingResultsIndexes, reg) => { const result = reg.results.find((s) => s.from_name === self); if (result?.hasValue) { existingResultsIndexes.add(reg.item_index); } return existingResultsIndexes; }, new Set()); for (let idx = 0; idx <= maxItemIndex; idx++) { if (!existingResultsIndexes.has(idx)) { objectTag.setCurrentItem(idx); self.requiredModal(); return false; } } } else { // validation when its classifying the whole object // isVisible can be undefined (so comparison is true) or boolean (so check for visibility) if (!self.holdsState && self.isVisible !== false && getParent(self, 2)?.isVisible !== false) { self.requiredModal(); return false; } } return true; }, }; }); export default RequiredMixin;