@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
231 lines (189 loc) • 6.03 kB
JavaScript
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;