Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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;