diff --git a/src/lib/utils/decorators/on-change.decorator.ts b/src/lib/utils/decorators/on-change.decorator.ts new file mode 100644 index 0000000..b083e4d --- /dev/null +++ b/src/lib/utils/decorators/on-change.decorator.ts @@ -0,0 +1,60 @@ +import { FunctionKeys } from '../types/utility-types'; + +export interface SimpleChange { + readonly previousValue: T; + readonly currentValue: T; + readonly isFirstChange: boolean; +} + +export type CallBackFunction = (value: T, change: SimpleChange) => void; + +type TypedPropertyDecorator = (target: C, key: PropertyKey) => void; + +const CACHED_VALUE_KEY = Symbol(); +const IS_FIRST_CHANGE_KEY = Symbol(); + +interface Instance { + [CACHED_VALUE_KEY]: T; + [IS_FIRST_CHANGE_KEY]: boolean; + + [key: string]: unknown; +} + +export function OnChange(callback: CallBackFunction | string): PropertyDecorator; +// eslint-disable-next-line @typescript-eslint/ban-types +export function OnChange(callback: CallBackFunction | FunctionKeys): TypedPropertyDecorator; +// eslint-disable-next-line @typescript-eslint/ban-types +export function OnChange(callback: CallBackFunction | FunctionKeys): TypedPropertyDecorator { + return function _onChange(target: C, key: PropertyKey) { + Object.defineProperty(target, key, { + set(value: T) { + const instance = this as Instance; + const callBackFn = >(typeof callback === 'string' ? instance[callback] : callback); + if (!callBackFn) { + throw new Error(`Cannot find method ${String(callback)} in class ${target.constructor.name}`); + } + + instance[IS_FIRST_CHANGE_KEY] = instance[IS_FIRST_CHANGE_KEY] === undefined; + + // No operation if new value is same as old value + if (!instance[IS_FIRST_CHANGE_KEY] && instance[CACHED_VALUE_KEY] === value) { + return; + } + + const oldValue = instance[CACHED_VALUE_KEY]; + instance[CACHED_VALUE_KEY] = value; + + const simpleChange: SimpleChange = { + previousValue: oldValue, + currentValue: instance[CACHED_VALUE_KEY], + isFirstChange: instance[IS_FIRST_CHANGE_KEY] + }; + + callBackFn.call(instance, instance[CACHED_VALUE_KEY], simpleChange); + }, + get(): T { + return (this as Instance)[CACHED_VALUE_KEY]; + } + }); + }; +} diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index e601a91..f721eff 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -8,3 +8,4 @@ export * from './types/tooltip-positions.type'; export * from './decorators/bind.decorator'; export * from './decorators/required.decorator'; export * from './decorators/debounce.decorator'; +export * from './decorators/on-change.decorator';