UNPKG

lazy-widgets

Version:

Typescript retained mode GUI for the HTML canvas API

225 lines 8.16 kB
import { DynMsg } from '../core/Strings.js'; /** * A decorator for a public field which sets calls a callback if the property's * value is changed. * * @typeParam V - The type of the field being watched * @param callback - The callback to call if the value changes. `this` is bound. * @category Decorator */ export function watchField(callback) { // eslint-disable-next-line @typescript-eslint/ban-types return function (target, propertyKey) { const curValues = new WeakMap(); Object.defineProperty(target, propertyKey, { set: function (value) { const oldValue = curValues.get(this); if (value !== oldValue) { curValues.set(this, value); callback.call(this, oldValue); } }, get: function () { return curValues.get(this); }, enumerable: true, configurable: true, }); }; } /** * A {@link watchField} which sets a given flag to true. * * @param flagKey - The key of the flag property to set to true * @category Decorator */ export function flagField(flagKey) { // eslint-disable-next-line @typescript-eslint/ban-types return watchField(function (_oldValue) { this[flagKey] = true; }); } /** * A {@link watchField} which calls a method named `markWholeAsDirty` with no * arguments. It's recommended to only use this on {@link Widget} and its * subclasses, although technically you can use it in any class so long as it * has a `markWholeAsDirty` method. * * @category Decorator */ // eslint-disable-next-line @typescript-eslint/ban-types export const damageField = watchField(function (_oldValue) { Object.getPrototypeOf(this).markWholeAsDirty.apply(this); }); /** * A {@link flagField} where the flag key is `_layoutDirty`. * * @category Decorator */ export const layoutField = flagField('_layoutDirty'); /** * A {@link watchField} which sets a given array of flags to true. * * @param flagKeys - An array containing the keys of each flag property to set to true * @category Decorator */ export function multiFlagField(flagKeys) { // eslint-disable-next-line @typescript-eslint/ban-types return watchField(function (_oldValue) { for (const flagKey of flagKeys) { this[flagKey] = true; } }); } /** * Similar to {@link watchField}, but for array fields, like tuples. Getting the * property returns a shallow copy of the tuple, setting the value uses a * shallow copy of the input value if the current value is not an array. If both * the new value and the current value are arrays, then the current value's * members are updated; no shallow copy is created. * * @param callback - The callback to call if the value changes. `this` is bound. * @param allowNonArrays - Allow values which are not arrays to be used? * @category Decorator */ export function watchArrayField(callback, allowNonArrays = false) { // eslint-disable-next-line @typescript-eslint/ban-types return function (target, propertyKey) { // eslint-disable-next-line @typescript-eslint/ban-types const curValues = new WeakMap(); Object.defineProperty(target, propertyKey, { set: function (value) { if (Array.isArray(value)) { const curTuple = curValues.get(this); if (Array.isArray(curTuple)) { if (value.length !== curTuple.length) { curTuple.length = value.length; for (let i = 0; i < value.length; i++) { curTuple[i] = value[i]; } callback.call(this); } else { for (let i = 0; i < value.length; i++) { if (curTuple[i] !== value[i]) { for (let j = 0; j < value.length; j++) { curTuple[j] = value[j]; } callback.call(this); return; } } } } else { curValues.set(this, [...value]); callback.call(this); } } else { if (allowNonArrays) { curValues.set(this, value); callback.call(this); } else { throw new Error(DynMsg.NON_ARRAY_VALUE(propertyKey, value)); } } }, get: function () { const curTuple = curValues.get(this); if (!Array.isArray(curTuple)) { return curTuple; } return [...curTuple]; }, enumerable: true, configurable: true, }); }; } /** * A {@link watchArrayField} which sets a given flag to true. * * @param flagKey - The key of the flag property to set to true * @param allowNonArrays - Allow values which are not arrays to be used? * @category Decorator */ export function flagArrayField(flagKey, allowNonArrays = false) { // eslint-disable-next-line @typescript-eslint/ban-types return watchArrayField(function () { this[flagKey] = true; }, allowNonArrays); } /** * A mix between {@link damageField} and {@link watchArrayField}. * * @param allowNonArrays - Allow values which are not arrays to be used? * @category Decorator */ export function damageArrayField(allowNonArrays = false) { // eslint-disable-next-line @typescript-eslint/ban-types return watchArrayField(function () { Object.getPrototypeOf(this).markWholeAsDirty.apply(this); }, allowNonArrays); } /** * A {@link flagArrayField} where the flag key is `_layoutDirty`. * * @param allowNonArrays - Allow values which are not arrays to be used? * @category Decorator */ export function layoutArrayField(allowNonArrays = false) { return flagArrayField('_layoutDirty', allowNonArrays); } /** * A {@link watchArrayField} which sets a given array of flags to true. * * @param flagKeys - An array containing the keys of each flag property to set to true * @param allowNonArrays - Allow values which are not arrays to be used? * @category Decorator */ export function multiFlagArrayField(flagKeys, allowNonArrays = false) { // eslint-disable-next-line @typescript-eslint/ban-types return watchArrayField(function () { for (const flagKey of flagKeys) { this[flagKey] = true; } }, allowNonArrays); } /** * A {@link watchArrayField} where the '_layoutDirty' flag is set, and a damage * method is called in the same way as {@link damageField}. * * @param allowNonArrays - Allow values which are not arrays to be used? * @category Decorator */ export function damageLayoutArrayField(allowNonArrays = false) { // eslint-disable-next-line @typescript-eslint/ban-types return watchArrayField(function () { this._layoutDirty = true; Object.getPrototypeOf(this).markWholeAsDirty.apply(this); }, allowNonArrays); } /** * A decorator for a public field which is an alias for another field. * * @param fieldName - The name of the field to create an alias for. * @category Decorator */ export function accessorAlias(fieldName) { // eslint-disable-next-line @typescript-eslint/ban-types return function (target, propertyKey) { Object.defineProperty(target, propertyKey, { set: function (value) { this[fieldName] = value; }, get: function () { return this[fieldName]; }, enumerable: true, configurable: true, }); }; } //# sourceMappingURL=FlagFields.js.map