UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

231 lines (189 loc) • 6.03 kB
import { assert } from "../../core/assert.js"; import { isTypedArray } from "../../core/collection/array/typed/isTypedArray.js"; import { noop } from "../../core/function/noop.js"; import { passThrough } from "../../core/function/passThrough.js"; import Vector1 from "../../core/geom/Vector1.js"; import BoundedValue from "../../core/model/BoundedValue.js"; import ObservedBoolean from "../../core/model/ObservedBoolean.js"; import ObservedInteger from "../../core/model/ObservedInteger.js"; import ObservedString from "../../core/model/ObservedString.js"; import ObservedValue from "../../core/model/ObservedValue.js"; import Stat from "../../core/model/stat/Stat.js"; import { number_pretty_print } from "../../core/primitives/numbers/number_pretty_print.js"; import { isInstanceOf } from "../../core/process/matcher/isInstanceOf.js"; import { isTypeOf } from "../../core/process/matcher/isTypeOf.js"; import { or } from "../../core/process/matcher/or.js"; import { frameThrottle } from "../../engine/graphics/FrameThrottle.js"; import View from "../View.js"; /** * * @param {Number|String|Boolean} value * @returns {*} */ function format(value) { if (typeof value === 'number') { return number_pretty_print(value); } else { return value; } } /** * * @param {string} v * @returns {string|number} */ function formatNumber(v) { return number_pretty_print(v); } function formatArray(arr) { return format(arr[0]) + " / " + format(arr[1]); } function extractorGetValue(m) { return m.getValue(); } function extractFunction(f) { return f(); } /** * * @param {BoundedValue} m */ function extractBoundedValue(m) { return [m.getValue(), m.getUpperLimit()]; } function arrayUnwrap(elements) { return elements.map(function (element) { const processor = findProcessor(element); const extractor = processor.extractor; return extractor(element); }); } /** * * @param model * @returns {ValueProcessor | undefined} */ function findProcessor(model) { return processors.find(function (p) { return p.matcher(model); }); } class ValueProcessor { /** * @template Container, Value * @param {function(Container):boolean} matcher * @param {function(Container):Value} extractor * @param {function(Value):string} formatter * @constructor */ constructor(matcher, extractor, formatter) { /** * * @type {function(*): boolean} */ this.matcher = matcher; /** * * @type {function(*): *} */ this.extractor = extractor; /** * * @type {function(*): string} */ this.formatter = formatter; } } /** * * @param {function(*):boolean} m * @param {function(*):*} e * @param {function(*):string} f * @returns {ValueProcessor} */ function p(m, e, f) { return new ValueProcessor(m, e, f); } /** * * @type {Array.<ValueProcessor>} */ const processors = [ p(isInstanceOf(ObservedBoolean), extractorGetValue, format), p(isInstanceOf(ObservedValue), extractorGetValue, format), p(isInstanceOf(ObservedString), extractorGetValue, format), p(isInstanceOf(BoundedValue), extractBoundedValue, formatArray), p(isInstanceOf(Stat), extractorGetValue, formatNumber), p(isInstanceOf(Vector1), extractorGetValue, formatNumber), p(isInstanceOf(ObservedInteger), extractorGetValue, formatNumber), p(or(isTypedArray, Array.isArray), arrayUnwrap, formatArray), p(isTypeOf("number"), passThrough, formatNumber), p(isTypeOf("string"), passThrough, passThrough), p(isTypeOf("boolean"), passThrough, passThrough), p(isTypeOf("function"), extractFunction, format), p(isTypeOf("undefined"), passThrough, passThrough) ]; class LabelView extends View { constructor(model, { classList = [], transform = passThrough, format = noop, tag = 'div', size, css } = {}) { super(); this.model = model; const processor = findProcessor(model); const extractor = processor.extractor; const formatter = format !== noop ? format : processor.formatter; assert.notEqual(extractor, null, `No extractor was found for ${typeof model}(${model})`); assert.notEqual(formatter, null, `No formatter was found for ${typeof model}(${model})`); this.__extractor = extractor; this.__formatter = formatter; this.__transform = transform; this.el = document.createElement(tag); this.el.classList.add('label'); for (const c of classList) { this.el.classList.add(c); } this.size.onChanged.add(this.updateSize, this); if (typeof this.model === "object" && this.model.onChanged !== undefined) { const throttledUpdate = frameThrottle(this.updateTransform, this); this.bindSignal(this.model.onChanged, throttledUpdate); } if (css !== undefined) { this.css(css); } if (size !== undefined) { this.size.fromJSON(size); } } /** * @private * @param {number} x * @param {number} y */ updateSize(x, y) { this.el.style.lineHeight = y + "px"; } /** * * @param {string} v */ updateText(v) { this.el.textContent = v; } updateTransform() { const model = this.model; const data = this.__extractor(model); const transformed = this.__transform(data); const text = this.__formatter(transformed); this.updateText(text); } link() { super.link(); this.updateTransform(); } } export default LabelView;