/** @format */ import {autorun, IReactionDisposer, spy} from 'mobx' import * as mobx from 'mobx' if (cc.sys.isBrowser) { ;(window as any).mobx = mobx } const getComponentOrNode = ( target: cc.Component, kv: Map, component: {prototype: T} | null, componentStr?: string, componentCb?: Function, ) => { let lastV: string kv.forEach((v, k) => { if (lastV && lastV == v) { console.error(component ? component.prototype.constructor.name : 'node', '注解错误有相同的url', lastV) } lastV = v if (component) { target[k] = cc.find(v, target.node)?.getComponent(component) } else if (componentStr) { target[k] = cc.find(v, target.node)?.getComponent(componentStr) } else { target[k] = cc.find(v, target.node) } componentCb && componentCb(target[k]) }) } export const observer = (constructor: T) => { return class extends constructor { _disposer: IReactionDisposer[] = [] _reaction?: string[] _autorun?: string[] _nodes?: Map _labels?: Map _editBoxes?: Map _lists?: Map _toggles?: Map onLoad() { if (this._nodes) { getComponentOrNode(this, this._nodes, null) } if (this._labels) { getComponentOrNode(this, this._labels, cc.Label) } if (this._editBoxes) { getComponentOrNode(this, this._editBoxes, cc.EditBox) } if (this._lists) { getComponentOrNode(this, this._lists, null, 'List', component => { component._init() }) } if (this._toggles) { getComponentOrNode(this, this._toggles, cc.Toggle) } super.onLoad && super.onLoad() if (this._autorun) { let callback = x => { this[x]() } this._disposer.push( ...this._autorun.map(x => autorun(() => { callback(x) }), ), ) } if (this._reaction) { this._disposer.push( ...this._reaction.map(x => { return (this as any)[x]() }), ) } } onEnable() { super.onEnable && super.onEnable() if (this._autorun) { let callback = x => { this[x]() } this._autorun.forEach(x => callback(x)) } } onDestroy() { super.onDestroy && super.onDestroy() if (this._disposer) this._disposer.forEach(x => x()) this._disposer.length = 0 } } } export const render = (target: any, key: string, descriptor: TypedPropertyDescriptor<() => void>) => { let obs: string[] = target['_autorun'] if (!obs) { obs = target['_autorun'] = [] } obs.push(key) } interface Reactor { (target: any, key: string, descriptor: TypedPropertyDescriptor<() => IReactionDisposer>): void (expression: (r: mobx.IReactionPublic) => T): ( target: any, key: string, descriptor: TypedPropertyDescriptor<(arg: T) => void>, ) => void } export const reactor: Reactor = function () { if (arguments.length === 3) { return reactor1(arguments[0], arguments[1], arguments[2]) } else { return reactor2(arguments[0]) } } as any const reactor1 = (target: any, key: string, descriptor: TypedPropertyDescriptor<() => IReactionDisposer>) => { let obs: string[] = target['_reaction'] if (!obs) { obs = target['_reaction'] = [] } obs.push(key) } const reactor2 = (expression: (r: mobx.IReactionPublic) => T) => { return (target: O, key: string, descriptor: TypedPropertyDescriptor<(arg: T) => void>) => { let obs = target['_reaction'] if (!obs) { obs = target['_reaction'] = [] } obs.push(key) const _value = descriptor.value as (arg: T) => void descriptor.value = function (this: O) { return mobx.reaction(expression.bind(this), _value.bind(this)) } } } /** * 和reactor搭配进行副作用操作 */ export const react = ( expression: (r: mobx.IReactionPublic) => T, effect: (arg: T, r: mobx.IReactionPublic) => void, ) => { return mobx.reaction(expression, effect, {fireImmediately: true}) } export const node = (arg: string) => { return (target: any, propertyKey: string) => { let nodes: Map = target['_nodes'] if (!nodes) { nodes = target['_nodes'] = new Map() } nodes.set(propertyKey, arg) } } export const label = (arg: string) => { return (target: any, propertyKey: string) => { let labels: Map = target['_labels'] if (!labels) { labels = target['_labels'] = new Map() } labels.set(propertyKey, arg) } } export const editBox = (arg: string) => { return (target: any, propertyKey: string) => { let editBoxes: Map = target['_editBoxes'] if (!editBoxes) { editBoxes = target['_editBoxes'] = new Map() } editBoxes.set(propertyKey, arg) } } export const list = (arg: string) => { return (target: any, propertyKey: string) => { let list: Map = target['_lists'] if (!list) { list = target['_lists'] = new Map() } list.set(propertyKey, arg) } } export const toggle = (arg: string) => { return (target: any, propertyKey: string) => { let toggle: Map = target['_toggles'] if (!toggle) { toggle = target['_toggles'] = new Map() } toggle.set(propertyKey, arg) } }