import { types } from "mobx-state-tree";
|
import React from "react";
|
import { FF_LSDV_4583, isFF } from "../../utils/feature-flags";
|
|
/**
|
* This is a mixin for a control-tag that is a base of creating classification-like tags.
|
* A classification tag is a tag that can be applied to the object tag and does not create a region related to the object-tag nature
|
* @see PerRegionMixin which allows to apply a created result to a region related to an object-tag nature
|
* @see PerItemMixin which allows to apply a created result to just one object-item provide by object-tag (@see MultiItemsMixin)
|
* */
|
const ClassificationBase = types
|
.model("ClassificationBase", {
|
isClassificationTag: true,
|
})
|
.extend((self) => {
|
/* Validation */
|
if (self.isControlTag !== true) {
|
throw new Error("The ClassificationBase mixin should be used only for ControlTags");
|
}
|
|
const REQUIRED_PROPERTIES = ["toname"];
|
const notDefinedProperties = REQUIRED_PROPERTIES.filter(
|
(name) => !self.$treenode.type.propertyNames.includes(name),
|
);
|
|
for (const notDefinedProperty of notDefinedProperties) {
|
throw new Error(
|
`The property "${notDefinedProperty}" should be defined for ClassificationBase mixin model needs`,
|
);
|
}
|
return {};
|
})
|
.volatile(() => ({
|
// for interactions with the tag, i.e. linking comments to classification
|
elementRef: React.createRef(),
|
}))
|
.views((self) => {
|
return {
|
selectedValues() {
|
throw new Error("ClassificationBase mixin model needs to implement selectedValues method in views");
|
},
|
|
get result() {
|
if (self.perregion) {
|
return self._perRegionResult;
|
}
|
if (self.peritem) {
|
return self._perItemResult;
|
}
|
return self.annotation.results.find((r) => r.from_name === self);
|
},
|
|
/**
|
* That name historically was used for regions, but now it's used for the classifications as well.
|
* Let's say "Region" here means just an area on the screen.
|
* So that it's an element through which we can get the bbox For an area where classification takes place.
|
*/
|
getRegionElement() {
|
return self.elementRef.current;
|
},
|
|
// Indicates that it could exist without information about objects, taskData and regions
|
get isIndependent() {
|
return self.isClassificationTag && !self.perregion && !self.peritem && !self.value;
|
},
|
};
|
})
|
.actions((self) => {
|
return {
|
/**
|
* Validates the input based on certain conditions.
|
*
|
* Generally, this method does not need to be overridden. And you need to override the validateValue method instead.
|
* However, there are exceptions. For example, RequiredMixin, Choices, and
|
* Taxonomy have their own additional logic, for which a broader context is needed.
|
* In this case, the parent method call is added at the beginning or end
|
* of the method to maintain all functionality in a predictable manner.
|
*
|
* @returns {boolean}
|
*/
|
validate() {
|
if (self.perregion) {
|
return self._validatePerRegion();
|
}
|
if (self.peritem && isFF(FF_LSDV_4583)) {
|
return self._validatePerItem();
|
}
|
return self._validatePerObject();
|
},
|
/**
|
* Validates the value.
|
*
|
* Override to add your custom validation logic specific for the tag.
|
* Per-item, per-region and per-object validation will be applied automatically.
|
*
|
* @example
|
* SomeModel.actions(self => {
|
* const Super = { validateValue: self.validateValue };
|
*
|
* return {
|
* validateValue(value) {
|
* if (!Super.validateValue(value)) return false;
|
* // your validation logic
|
* }
|
* // other actions
|
* }
|
* });
|
*
|
* @param {*} value - The value to be validated.
|
* @returns {boolean}
|
*
|
*/
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
validateValue(value) {
|
return true;
|
},
|
/**
|
* Validates all values related to the current classification per object.
|
*
|
* - This method should not be overridden.
|
* - It is used only in validate method of the ClassificationBase mixin.
|
*
|
* @returns {boolean}
|
* @private
|
*/
|
_validatePerObject() {
|
return self.validateValue(self.selectedValues());
|
},
|
createPerObjectResult(areaValues = {}) {
|
self.annotation.createResult(areaValues, { [self.valueType]: self.selectedValues() }, self, self.toname);
|
},
|
|
// update result in the store with current set value
|
updateResult() {
|
if (self.result) {
|
self.result.area.updateOriginOnEdit();
|
self.result.area.setValue(self);
|
} else {
|
if (self.perregion) {
|
self.createPerRegionResult?.();
|
} else if (self.peritem) {
|
self.createPerItemResult();
|
} else {
|
self.createPerObjectResult();
|
}
|
}
|
},
|
};
|
});
|
|
export default ClassificationBase;
|