diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index 10922ca14a..55770bb713 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -69,7 +69,7 @@ export class UIEventSource { * @param promise * @constructor */ - public static FromPromise(promise : Promise): UIEventSource{ + public static FromPromise(promise: Promise): UIEventSource { const src = new UIEventSource(undefined) promise?.then(d => src.setData(d)) promise?.catch(err => console.warn("Promise failed:", err)) @@ -82,9 +82,9 @@ export class UIEventSource { * @param promise * @constructor */ - public static FromPromiseWithErr(promise : Promise): UIEventSource<{success: T} | {error: any}>{ - const src = new UIEventSource<{success: T}|{error: any}>(undefined) - promise?.then(d => src.setData({success:d})) + public static FromPromiseWithErr(promise: Promise): UIEventSource<{ success: T } | { error: any }> { + const src = new UIEventSource<{ success: T } | { error: any }>(undefined) + promise?.then(d => src.setData({success: d})) promise?.catch(err => src.setData({error: err})) return src } @@ -97,42 +97,42 @@ export class UIEventSource { * src.addCallback(_ => console.log("src pinged")) * stable.addCallback(_ => console.log("stable pinged)) * src.setDate([...src.data]) - * + * * This will only trigger 'src pinged' - * + * * @param src * @constructor */ - public static ListStabilized(src: UIEventSource) : UIEventSource{ - + public static ListStabilized(src: UIEventSource): UIEventSource { + const stable = new UIEventSource(src.data) src.addCallback(list => { - if(list === undefined){ + if (list === undefined) { stable.setData(undefined) return; } const oldList = stable.data - if(oldList === list){ + if (oldList === list) { return; } - if(oldList.length !== list.length){ + if (oldList.length !== list.length) { stable.setData(list); return; } for (let i = 0; i < list.length; i++) { - if(oldList[i] !== list[i]){ + if (oldList[i] !== list[i]) { stable.setData(list); return; } } - + // No actual changes, so we don't do anything return; }) return stable } - + /** * Adds a callback * @@ -190,21 +190,26 @@ export class UIEventSource { /** * Monadic bind function */ - public bind(f: ((t: T) => UIEventSource)): UIEventSource{ - const sink = new UIEventSource( undefined ) + public bind(f: ((t: T) => UIEventSource)): UIEventSource { + const mapped = this.map(f) + const sink = new UIEventSource(undefined) const seenEventSources = new Set>(); - this.addCallbackAndRun(data => { - const eventSource = f(data) - if(eventSource === undefined){ + mapped.addCallbackAndRun(newEventSource => { + + if (newEventSource === undefined) { sink.setData(undefined) - }else if(!seenEventSources.has(eventSource)){ - eventSource.addCallbackAndRun(mappedData => sink.setData(mappedData)) - seenEventSources.add(eventSource) + } else if (!seenEventSources.has(newEventSource)) { + seenEventSources.add(newEventSource) + newEventSource.addCallbackAndRun(resultData => { + if (mapped.data === newEventSource) { + sink.setData(resultData); + } + }) } }) - + return sink; - } + } /** * Monoidal map: diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 576189175d..e95d3abfe7 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -70,7 +70,10 @@ export default class TagRenderingQuestion extends Combine { return TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options) }) )*/ - super([TagRenderingQuestion.GenerateFullQuestion(tags, (configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined), configuration, options)]) + + const applicableMappings = Utils.NoNull((configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined)) + + super([TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options)]) } private static GenerateFullQuestion(tags: UIEventSource, @@ -163,6 +166,13 @@ export default class TagRenderingQuestion extends Combine { applicableUnit: Unit, tagsSource: UIEventSource) : InputElement { + + // FreeForm input will be undefined if not present; will already contain a special input element if applicable + const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); + + + + const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 let inputEls: InputElement[]; @@ -173,11 +183,11 @@ export default class TagRenderingQuestion extends Combine { return undefined } if (!ifNotsPresent) { - return undefined + return [] } if (configuration.multiAnswer) { // The multianswer will do the ifnot configuration themself - return undefined + return [] } const negativeMappings = [] @@ -193,9 +203,7 @@ export default class TagRenderingQuestion extends Combine { } - const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); - const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 - + if (applicableMappings.length < 8 || configuration.multiAnswer || hasImages || ifNotsPresent) { inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); inputEls = Utils.NoNull(inputEls); @@ -226,7 +234,7 @@ export default class TagRenderingQuestion extends Combine { } if (configuration.multiAnswer) { - return TagRenderingQuestion.GenerateMultiAnswer(configuration, inputEls, ff, configuration.mappings.map(mp => mp.ifnot)) + return TagRenderingQuestion.GenerateMultiAnswer(configuration, inputEls, ff, applicableMappings.map(mp => mp.ifnot)) } else { return new RadioButton(inputEls, {selectFirstAsDefault: false}) }