import { computed } from "mobx"; import ModelTraits from "../../Traits/ModelTraits"; import Stratified from "../../Traits/Stratified"; import TraitsConstructor from "../../Traits/TraitsConstructor"; import ModelPropertiesFromTraits from "./ModelPropertiesFromTraits"; /** * Adds a set of traits to a model. The values of the traits will be derived * from the model's strata. * @param model The model class or instance. * @param Traits The traits class. * @returns The original model, now modified to add the added traits. */ export default function addModelStrataView< T extends TraitsConstructor >( /* TODO: Use a more specific type than "Function". */ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type model: Stratified> | Function, Traits: T ): ModelPropertiesFromTraits>; export default function addModelStrataView< T extends TraitsConstructor >(model: any, Traits: T): ModelPropertiesFromTraits> { const traits = Traits.traits; const decorators: { [id: string]: PropertyDecorator } = {}; const propertyTarget = typeof model === "function" ? model.prototype : model; const traitsInstance: any = new Traits(); Object.keys(traits).forEach((traitName) => { const trait = traits[traitName]; const defaultValue = traitsInstance[traitName]; Object.defineProperty(propertyTarget, traitName, { get: function () { const value = trait.getValue(this); return value === undefined ? defaultValue : value; }, enumerable: true, configurable: true }); decorators[traitName] = trait.decoratorForFlattened || computed; }); decorate(model, decorators); return model; } /** * Decorate the target class. * * Note that we assume that the class constructor calls `makeObservable(this)` * to correctly setup the mobx properties. * * @param target Target class to decorate * @param decorators Properties of the class that must be decorated */ function decorate( target: any, decorators: { [id: string]: PropertyDecorator } ) { Object.entries(decorators).forEach(([prop, decorator]) => { decorator(target.prototype, prop); }); }