Trying to get the checkboxlogic right

This commit is contained in:
Pieter Vander Vennet 2020-09-10 21:06:56 +02:00
parent c944156d87
commit e0f2f70c2e
7 changed files with 113 additions and 97 deletions

View file

@ -462,6 +462,7 @@ export class TagUtils {
let leftoverTag = undefined; let leftoverTag = undefined;
if (keyValues[freeformKey] !== undefined && keyValues[freeformKey].length !== 0) { if (keyValues[freeformKey] !== undefined && keyValues[freeformKey].length !== 0) {
leftoverTag = new Tag(freeformKey, keyValues[freeformKey].join(";")); leftoverTag = new Tag(freeformKey, keyValues[freeformKey].join(";"));
console.log("Leftovers are ", leftoverTag)
if (freeformExtraTags !== undefined) { if (freeformExtraTags !== undefined) {
leftoverTag = new And([ leftoverTag = new And([
leftoverTag, leftoverTag,

View file

@ -1,52 +1,36 @@
import {InputElement} from "./InputElement"; import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource"; import {UIEventSource} from "../../Logic/UIEventSource";
import {Utils} from "../../Utils"; import {Utils} from "../../Utils";
import {UIElement} from "../UIElement";
/** /**
* Supports multi-input * Supports multi-input
*/ */
export class CheckBoxes<T> extends InputElement<T[]> { export class CheckBoxes extends InputElement<number[]> {
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _selectedElementIndex: UIEventSource<number> private readonly value: UIEventSource<number[]>;
= new UIEventSource<number>(null); private readonly _elements: UIElement[]
private readonly value: UIEventSource<T[]>;
private readonly _elements: InputElement<T>[]
constructor(elements: InputElement<T>[]) { constructor(elements: UIElement[]) {
super(undefined); super(undefined);
this._elements = Utils.NoNull(elements); this._elements = Utils.NoNull(elements);
this.dumbMode = false; this.dumbMode = false;
this.value = new UIEventSource<T[]>([]) this.value = new UIEventSource<number[]>([])
this.ListenTo(this.value); this.ListenTo(this.value);
this.value.addCallback(latest => console.log("Latest is ", latest))
} }
IsValid(ts: T[]): boolean {
IsValid(ts: number[]): boolean {
if (ts === undefined) { if (ts === undefined) {
return false; return false;
} }
for (const t of ts) {
let matchFound = false;
for (const element of this._elements) {
if (element.IsValid(t)) {
element.GetValue().setData(t);
matchFound = true;
break
}
}
if (!matchFound) {
return false;
}
}
return true; return true;
} }
GetValue(): UIEventSource<T[]> { GetValue(): UIEventSource<number[]> {
return this.value; return this.value;
} }
@ -72,39 +56,25 @@ export class CheckBoxes<T> extends InputElement<T[]> {
super.InnerUpdate(htmlElement); super.InnerUpdate(htmlElement);
const self = this; const self = this;
for (let i = 0; i < this._elements.length; i++) { for (let i = 0; i < this._elements.length; i++) {
const el = document.getElementById(this.IdFor(i)); const el = document.getElementById(this.IdFor(i));
const inputEl = this._elements[i];
for (const t of this.value.data ?? []) { if(this.value.data.indexOf(i) >= 0){
if(t === undefined){
continue;
}
let isValid = inputEl.IsValid(t);
// @ts-ignore // @ts-ignore
el.checked = isValid; el.checked = true;
if(isValid){
break;
}
} }
el.onchange = e => { el.onchange = () => {
const v = inputEl.GetValue().data; const index = self.value.data.indexOf(i);
const index = self.value.data.indexOf(v);
// @ts-ignore // @ts-ignore
if (el.checked) { if(el.checked && index < 0){
if (index < 0) { self.value.data.push(i);
self.value.data.push(v);
self.value.ping(); self.value.ping();
} }else if(index >= 0){
} else {
if (index >= 0) {
self.value.data.splice(index,1); self.value.data.splice(index,1);
self.value.ping(); self.value.ping();
} }
} }
}
} }

View file

@ -26,8 +26,6 @@ export class FixedInputElement<T> extends InputElement<T> {
} }
IsValid(t: T): boolean { IsValid(t: T): boolean {
console.log("Comparing ",t, "with", this.value.data);
return this._comparator(t, this.value.data); return this._comparator(t, this.value.data);
} }

View file

@ -152,7 +152,9 @@ export class TextField<T> extends InputElement<T> {
this.ListenTo(this._placeholder._source); this.ListenTo(this._placeholder._source);
this._toString = options.toString ?? ((t) => ("" + t)); this._toString = options.toString ?? ((t) => ("" + t));
this.onClick(() => {
self.IsSelected.setData(true)
});
this.mappedValue.addCallback((t) => { this.mappedValue.addCallback((t) => {
if (t === undefined || t === null) { if (t === undefined || t === null) {
return; return;
@ -202,6 +204,7 @@ export class TextField<T> extends InputElement<T> {
field.addEventListener("focusin", () => self.IsSelected.setData(true)); field.addEventListener("focusin", () => self.IsSelected.setData(true));
field.addEventListener("focusout", () => self.IsSelected.setData(false)); field.addEventListener("focusout", () => self.IsSelected.setData(false));
field.addEventListener("keyup", function (event) { field.addEventListener("keyup", function (event) {
if (event.key === "Enter") { if (event.key === "Enter") {
// @ts-ignore // @ts-ignore

View file

@ -205,7 +205,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
InputElement<TagsFilter> { InputElement<TagsFilter> {
let freeformElement = undefined; let freeformElement: InputElement<TagsFilter> = undefined;
if (options.freeform !== undefined) { if (options.freeform !== undefined) {
freeformElement = this.InputForFreeForm(options.freeform); freeformElement = this.InputForFreeForm(options.freeform);
} }
@ -235,45 +235,61 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
const possibleTags = elements.map(el => el.GetValue().data); const possibleTags = elements.map(el => el.GetValue().data);
const checkBoxes = new CheckBoxes(elements); const checkBoxes = new CheckBoxes(elements);
// In order to let this work, we are cheating a lot here
// First of all, it is very tricky to let the mapping stabilize const inputEl = new InputElementMap<number[], TagsFilter>(checkBoxes,
// The selection gets added as list and needs to be flattened into a new 'and' (t0, t1) => {
// This new and causes the mapping to have a 'set value', which is unpacked to update the UI return t0?.isEquivalent(t1) ?? false
// AFter which the UI reupdates and reapplies the value
// So, instead we opt to _always return the 'value' below which is statefully updated
// But, then we still have to figure out when to update...
// For this, we use the original inputElement
// This is very dirty code, I know
const value = new And([]);
const inputElement = new InputElementMap(checkBoxes,
(t0: And, t1: And) => {
return t0?.isEquivalent(t1) ?? t0 === t1
}, },
(fromUI) => { (indices) => {
if (fromUI === undefined) { if (indices.length === 0) {
value.and = []; return undefined;
return value;
} }
const flattened = TagUtils.FlattenMultiAnswer(fromUI); let tags: TagsFilter[] = indices.map(i => elements[i].GetValue().data);
value.and = flattened.and; return TagUtils.FlattenMultiAnswer(tags);
return value;
}, },
(fromTags) => { (tags: TagsFilter) => {
return TagUtils.SplitMultiAnswer(fromTags, possibleTags, this._freeform?.key, this._freeform?.extraTags); const splitUpValues = TagUtils.SplitMultiAnswer(tags, possibleTags, this._freeform?.key, this._freeform?.extraTags);
const indices: number[] = []
for (let i = 0; i < splitUpValues.length; i++) {
let splitUpValue = splitUpValues[i];
for (let j = 0; j < elements.length; j++) {
let inputElement = elements[j];
if (inputElement.IsValid(splitUpValue)) {
indices.push(j);
inputElement.GetValue().setData(splitUpValue);
break;
}
}
}
return indices;
} }
); );
let previousSelectionCount = -1; freeformElement?.IsSelected.addCallback((isSelected) => {
checkBoxes.GetValue().addCallback(selected => { console.log("SELECTED FF", isSelected)
const newSelectionCount = selected.length; if (isSelected) {
if (newSelectionCount != previousSelectionCount) { const es = checkBoxes.GetValue();
previousSelectionCount = newSelectionCount; const i = elements.length - 1
inputElement.GetValue().ping(); if (es.data.indexOf(i) >= 0) {
return;
}
es.data.push(i);
es.ping();
} }
}); });
return inputElement; freeformElement?.GetValue()?.addCallback(() => {
const es = checkBoxes.GetValue();
const i = elements.length - 1
if (es.data.indexOf(i) < 0) {
es.data.push(i);
es.ping();
}
});
return inputEl;
} }
return new RadioButton(elements, false); return new RadioButton(elements, false);
} }
@ -340,9 +356,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
); );
}; };
const toString = const toString = (tag) => {
(tag) => {
console.log("Decoding ", tag, "in freeform text element")
if (tag instanceof And) { if (tag instanceof And) {
for (const subtag of tag.and) { for (const subtag of tag.and) {
if (subtag instanceof Tag && subtag.key === freeform.key) { if (subtag instanceof Tag && subtag.key === freeform.key) {

View file

@ -95,8 +95,12 @@ export abstract class UIElement extends UIEventSource<string> {
return; return;
} }
this.setData(this.lastInnerRender ?? this.InnerRender()); const newRender = this.InnerRender();
if (newRender !== this.lastInnerRender) {
this.setData(this.InnerRender());
element.innerHTML = this.data; element.innerHTML = this.data;
this.lastInnerRender = newRender;
}
if (this._hideIfEmpty) { if (this._hideIfEmpty) {
if (element.innerHTML === "") { if (element.innerHTML === "") {

26
clean.sh Executable file
View file

@ -0,0 +1,26 @@
#! /bin/bash
# clean up the mess we made
# rm *.js
# rm Logic/*.js
# rm Logic/*.js
# rm Logic/*/*.js
# rm Logic/*/*/*.js
# rm UI/*.js
# rm UI/*/*.js
# rm UI/*/*/*.js
# rm Customizations/*.js
# rm Customizations/*/*.js
# rm Customizations/*/*/*.js
rm *.webmanifest
rm assets/generated/*
for f in ./*.html; do
if [[ "$f" == "./index.html" ]] || [[ "$f" == "./land.html" ]] || [[ "$f" == "./test.html" ]] || [[ "$f" == "./preferences.html" ]] || [[ "$f" == "./customGenerator.html" ]]
then
echo "Not removing $f"
else
rm $f
fi
done