Files
Web-NibblePoker/tools/items/formula-wizard/src/mvc_formula.ts

359 lines
14 KiB
TypeScript

/*
* Formula Wizard v0.0.2
* [Short desc here]
* https://github.com/aziascreations/Web-NibblePoker
* Copyright (c) 2023 Herwin Bozet <herwin.bozet@gmail.com>
*/
import {localize} from "./lang";
import {populateScaleSelectForUnit} from "./units";
import {isCatalogInitialized, catalogFormulas} from "./ui_catalog";
import {FormulaValue, FormulaVariant} from "./formulas";
import {retrieveTemplate, makeElementFromFragment, appendToAllIds} from "./utils_templates";
// -----------
// Constants
// -----------
const ID_TEMPLATE_FORMULA = "template-workbench-formula";
const ID_TEMPLATE_FORMULA_VALUE = ID_TEMPLATE_FORMULA + "-value";
const ID_FORMULA_PREFIX = "fw-workbench-formula-";
const ID_FORMULA_SPAWN_POINT = ID_FORMULA_PREFIX + "spawn";
const ID_FORMULA_NAME = ID_FORMULA_PREFIX + "name";
const ID_FORMULA_INPUTS = ID_FORMULA_PREFIX + "inputs";
const ID_FORMULA_OUTPUTS = ID_FORMULA_PREFIX + "outputs";
const ID_FORMULA_VALUE_PREFIX = ID_FORMULA_PREFIX + "value-";
const ID_FORMULA_VALUE_ID = ID_FORMULA_VALUE_PREFIX + "id";
const ID_FORMULA_VALUE_NAME = ID_FORMULA_VALUE_PREFIX + "name";
const ID_FORMULA_VALUE_LINK = ID_FORMULA_VALUE_PREFIX + "link";
const ID_FORMULA_VALUE_TEST_SEPARATOR_TITLE = ID_FORMULA_VALUE_PREFIX + "test-separator-title";
const ID_FORMULA_VALUE_TEST_VALUE = ID_FORMULA_VALUE_PREFIX + "test-value";
const ID_FORMULA_VALUE_TEST_SCALE = ID_FORMULA_VALUE_PREFIX + "test-scale";
const ID_FORMULA_VALUE_TEST_VALUE_SET = ID_FORMULA_VALUE_PREFIX + "test-value-set";
// ---------
// Globals
// ---------
let eTemplateWorkbenchFormula: DocumentFragment | null = null;
let eTemplateWorkbenchFormulaValue: DocumentFragment | null = null;
export let workbenchFormulas: WorkbenchFormula[] = [];
// ----------------------
// Global-related utils
// ----------------------
function deleteWorkbenchFormula(deletedFormula: WorkbenchFormula, removeDom: boolean = true) {
workbenchFormulas = workbenchFormulas.filter(item => item !== deletedFormula);
if(removeDom) {
deletedFormula.uiElement.eRootElement.remove();
}
}
function handleCatalogInsertion(formulaVariant: FormulaVariant, eSpawnPointAnchor: HTMLAnchorElement) {
console.group("Adding new formula to workbench...");
console.debug("Instantiating 'WorkbenchFormula'...");
const newWorkbenchFormula = new WorkbenchFormula(
eTemplateWorkbenchFormula as DocumentFragment,
eTemplateWorkbenchFormulaValue as DocumentFragment,
formulaVariant
);
console.debug("Registering controller...");
workbenchFormulas.push(newWorkbenchFormula);
console.debug("Adding to workbench's DOM...");
eSpawnPointAnchor!.parentNode!.insertBefore(newWorkbenchFormula.uiElement.eRootElement, eSpawnPointAnchor);
console.debug(newWorkbenchFormula);
console.groupEnd();
}
// --------------------
// Formula Values MVC
// --------------------
enum EWorkbenchFormulaValueTypes {
//UNKNOWN,
INPUT,
OUTPUT,
}
class WorkbenchFormulaValueData {
id: string;
valueType: EWorkbenchFormulaValueTypes
formulaValue: FormulaValue;
constructor(controllerId: string, valueType: EWorkbenchFormulaValueTypes, formulaValue: FormulaValue) {
this.id = controllerId;
this.valueType = valueType;
this.formulaValue = formulaValue;
}
}
class WorkbenchFormulaValueInterface {
public eRootElement: HTMLElement;
private readonly eFormulaValueId: HTMLInputElement;
private readonly eFormulaValueName: HTMLParagraphElement;
private readonly eFormulaValueLink: HTMLSelectElement;
private readonly eFormulaValueTestTitleSeparator: HTMLParagraphElement;
private readonly eFormulaValueTestValue: HTMLInputElement;
public readonly eFormulaValueTestScale: HTMLSelectElement;
private readonly eFormulaValueTestValueSet: HTMLInputElement;
constructor(eRootFragment: DocumentFragment, setupValueType: EWorkbenchFormulaValueTypes) {
this.eRootElement = makeElementFromFragment(eRootFragment);
// Grabbing references to all the form's fields.
this.eFormulaValueId = this.eRootElement.querySelector(`input#${ID_FORMULA_VALUE_ID}`)!;
this.eFormulaValueName = this.eRootElement.querySelector(`p#${ID_FORMULA_VALUE_NAME}`)!;
this.eFormulaValueLink = this.eRootElement.querySelector(`select#${ID_FORMULA_VALUE_LINK}`)!;
this.eFormulaValueTestTitleSeparator = this.eRootElement.querySelector(`p#${ID_FORMULA_VALUE_TEST_SEPARATOR_TITLE}`)!;
this.eFormulaValueTestValue = this.eRootElement.querySelector(`input#${ID_FORMULA_VALUE_TEST_VALUE}`)!;
this.eFormulaValueTestScale = this.eRootElement.querySelector(`select#${ID_FORMULA_VALUE_TEST_SCALE}`)!;
this.eFormulaValueTestValueSet = this.eRootElement.querySelector(`select#${ID_FORMULA_VALUE_TEST_VALUE_SET}`)!;
if([this.eFormulaValueId, this.eFormulaValueName, this.eFormulaValueLink, this.eFormulaValueTestValue,
this.eFormulaValueTestTitleSeparator, this.eFormulaValueTestScale, this.eFormulaValueTestValueSet].some(
(item) => item === null)) {
alert("error.ui.formula.value.missingElement");
throw Error("error.ui.formula.value.missingElement");
}
//// Adding the relevant scale factors.
//populateScaleSelectForUnit(this.formulaValue.unit, this.eFormulaValueTestScale, this.formulaValue.scaleFactor);
//if(setupValueType === EWorkbenchFormulaValueTypes.INPUT) {
// this.setupInput();
//} else {
// this.setupOutput();
//}
}
toggleField(eFormField: HTMLElement, hidden: boolean) {
// We hide the parent "tr" element.
(eFormField.parentNode!.parentNode! as HTMLElement).hidden = hidden;
}
setupInput() {
this.toggleField(this.eFormulaValueId, true);
this.eFormulaValueTestValue.value = "0";
//this.eFormulaValueTestValue.onchange = this.onTestFieldChange.bind(this);
//this.eFormulaValueTestScale.onchange = this.onTestFieldChange.bind(this);
}
setupOutput() {
this.toggleField(this.eFormulaValueLink, true);
//this.eFormulaValueTestValue.readOnly = true;
//this.eFormulaValueTestScale.onchange = this.onTestFieldChange.bind(this);
}
setName(newName: string) {
this.eFormulaValueName.innerText = newName;
}
changeTestMode(newStatus: boolean) {
this.toggleField(this.eFormulaValueTestTitleSeparator, !newStatus);
this.toggleField(this.eFormulaValueTestValue, !newStatus);
this.toggleField(this.eFormulaValueTestScale, !newStatus);
this.toggleField(this.eFormulaValueTestValueSet, !newStatus);
}
}
class WorkbenchFormulaValue {
data: WorkbenchFormulaValueData;
uiElement: WorkbenchFormulaValueInterface;
constructor(eRootFragment: DocumentFragment, valueType: EWorkbenchFormulaValueTypes,
parentAssignedControllerId: string, formulaValue: FormulaValue) {
this.data = new WorkbenchFormulaValueData(parentAssignedControllerId, valueType, formulaValue);
this.uiElement = new WorkbenchFormulaValueInterface(eRootFragment, valueType);
// Preparing the UI.
this.setName(`${this.data.formulaValue.unit.name} (${this.data.formulaValue.unit.symbol})`);
// Adding the relevant scale factors to "eFormulaValueTestScale" based on the formula variant's unit.
populateScaleSelectForUnit(
this.data.formulaValue.unit,
this.uiElement.eFormulaValueTestScale,
this.data.formulaValue.scaleFactor
);
// Making sided setups.
if (this.data.valueType === EWorkbenchFormulaValueTypes.INPUT) {
// Setting up as input.
this.uiElement.setupInput();
} else {
// Setting up as output.
this.uiElement.setupOutput();
}
// Finalizing the interface by making its IDs unique.
// This is purely a UX thing to ensure proper input selection via the labels.
appendToAllIds(this.uiElement.eRootElement, this.data.id);
}
setName(newName: string) {
this.uiElement.setName(newName);
}
changeTestMode(newStatus: boolean) {
this.uiElement.changeTestMode(newStatus);
}
}
// --------------
// Formulas MVC
// --------------
class WorkbenchFormulaData {
id: string;
formulaVariant: FormulaVariant;
isTestModeEnabled: boolean;
constructor(controllerId: string, formulaVariant: FormulaVariant) {
this.id = controllerId;
this.formulaVariant = formulaVariant;
this.isTestModeEnabled = false;
}
}
class WorkbenchFormulaInterface {
public eRootElement: HTMLElement;
private readonly eTitle: HTMLParagraphElement;
public readonly eInputsContainer: HTMLDivElement;
public readonly eOutputsContainer: HTMLDivElement;
constructor(eRootFragment: DocumentFragment) {
this.eRootElement = makeElementFromFragment(eRootFragment);
this.eTitle = this.eRootElement.querySelector(`p#${ID_FORMULA_NAME}`)!;
this.eInputsContainer = this.eRootElement.querySelector(`div#${ID_FORMULA_INPUTS}`)!;
this.eOutputsContainer = this.eRootElement.querySelector(`div#${ID_FORMULA_OUTPUTS}`)!;
if([this.eTitle, this.eInputsContainer, this.eOutputsContainer].some((item) => item === null)) {
alert(localize("error.ui.formula.missingElement"));
throw Error(localize("error.ui.formula.missingElement"));
}
}
setTitle(newTitle: string) {
this.eTitle.innerText = newTitle;
}
}
class WorkbenchFormula {
uiElement: WorkbenchFormulaInterface;
data: WorkbenchFormulaData;
inputControllers: WorkbenchFormulaValue[];
outputControllers: WorkbenchFormulaValue[];
constructor(fragRootElement: DocumentFragment, fragValueElement: DocumentFragment, formulaVariant: FormulaVariant) {
const id: string = Date.now().toString();
// Preparing core MVC components.
this.uiElement = new WorkbenchFormulaInterface(fragRootElement);
this.data = new WorkbenchFormulaData(id, formulaVariant);
// Preparing children MVC components.
let valueIdSuffix = 0;
this.inputControllers = this.data.formulaVariant.getInputValuesDefinition().map(
inputFormulaValue => new WorkbenchFormulaValue(
fragValueElement,
EWorkbenchFormulaValueTypes.INPUT,
this.data.id + (valueIdSuffix++).toString(),
inputFormulaValue
)
);
this.outputControllers = [this.data.formulaVariant.getOutputValueDefinition()].map(
outputFormulaValue => new WorkbenchFormulaValue(
fragValueElement,
EWorkbenchFormulaValueTypes.OUTPUT,
this.data.id + (valueIdSuffix++).toString(),
outputFormulaValue
)
);
// Preparing the interface...
this.setTitle(this.data.formulaVariant.parentFormula.name);
// Disabling the test mode by default.
this.changeTestMode(false);
// Adding the value elements into their proper container.
this.inputControllers.every(inputController =>
this.uiElement.eInputsContainer.appendChild(inputController.uiElement.eRootElement));
this.outputControllers.every(outputController =>
this.uiElement.eOutputsContainer.appendChild(outputController.uiElement.eRootElement));
}
public setTitle(newTitle: string) {
this.uiElement.setTitle(newTitle);
}
public changeTestMode(newStatus: boolean) {
this.data.isTestModeEnabled = newStatus;
this.inputControllers.forEach(inputController => inputController.changeTestMode(newStatus));
this.outputControllers.forEach(outputController => outputController.changeTestMode(newStatus));
}
}
// ----------------
// ???
// ----------------
let isWorkbenchFormulaSetup = false;
export function setupWorkbenchFormula() {
if (!isWorkbenchFormulaSetup) {
console.group("Preparing UI for workbench formula components...");
console.debug("Checking if the catalog is ready...");
if(!isCatalogInitialized()) {
console.groupEnd();
alert(localize("error.ui.workbench.catalog.no-init"));
throw Error(localize("error.ui.workbench.catalog.no-init"));
}
console.debug("Grabbing, processing & removing the templates from the DOM...");
eTemplateWorkbenchFormula = retrieveTemplate(ID_TEMPLATE_FORMULA, true);
eTemplateWorkbenchFormulaValue = retrieveTemplate(ID_TEMPLATE_FORMULA_VALUE, true);
console.debug("Grabbing the anchor point for new formulas...");
const eWorkbenchFormulaSpawnPoint: HTMLAnchorElement | null = document.querySelector(`a#${ID_FORMULA_SPAWN_POINT}`);
if(eWorkbenchFormulaSpawnPoint === null) {
console.groupEnd();
alert(localize("error.ui.workbench.noAnchor"));
throw Error(localize("error.ui.workbench.noAnchor"));
}
console.debug("Preparing the catalog's buttons actions & inserting them in the DOM");
catalogFormulas.forEach(catalogFormula => {
for(let iVariant = 0; iVariant < catalogFormula.formula.variants.length; iVariant++) {
catalogFormula.eVariantButtons[iVariant].onclick = function (){
handleCatalogInsertion(catalogFormula.formula.variants[iVariant], eWorkbenchFormulaSpawnPoint);
}
}
catalogFormula.insertIntoCategories();
});
isWorkbenchFormulaSetup = true;
console.groupEnd();
}
}