Fix bug in bind, fix bug in tagrenderingquestion

This commit is contained in:
Pieter Vander Vennet 2021-10-03 20:50:18 +02:00
parent 2813c31a93
commit b6b20ed3ca
2 changed files with 44 additions and 31 deletions

View file

@ -69,7 +69,7 @@ export class UIEventSource<T> {
* @param promise * @param promise
* @constructor * @constructor
*/ */
public static FromPromise<T>(promise : Promise<T>): UIEventSource<T>{ public static FromPromise<T>(promise: Promise<T>): UIEventSource<T> {
const src = new UIEventSource<T>(undefined) const src = new UIEventSource<T>(undefined)
promise?.then(d => src.setData(d)) promise?.then(d => src.setData(d))
promise?.catch(err => console.warn("Promise failed:", err)) promise?.catch(err => console.warn("Promise failed:", err))
@ -82,9 +82,9 @@ export class UIEventSource<T> {
* @param promise * @param promise
* @constructor * @constructor
*/ */
public static FromPromiseWithErr<T>(promise : Promise<T>): UIEventSource<{success: T} | {error: any}>{ public static FromPromiseWithErr<T>(promise: Promise<T>): UIEventSource<{ success: T } | { error: any }> {
const src = new UIEventSource<{success: T}|{error: any}>(undefined) const src = new UIEventSource<{ success: T } | { error: any }>(undefined)
promise?.then(d => src.setData({success:d})) promise?.then(d => src.setData({success: d}))
promise?.catch(err => src.setData({error: err})) promise?.catch(err => src.setData({error: err}))
return src return src
} }
@ -97,42 +97,42 @@ export class UIEventSource<T> {
* src.addCallback(_ => console.log("src pinged")) * src.addCallback(_ => console.log("src pinged"))
* stable.addCallback(_ => console.log("stable pinged)) * stable.addCallback(_ => console.log("stable pinged))
* src.setDate([...src.data]) * src.setDate([...src.data])
* *
* This will only trigger 'src pinged' * This will only trigger 'src pinged'
* *
* @param src * @param src
* @constructor * @constructor
*/ */
public static ListStabilized<T>(src: UIEventSource<T[]>) : UIEventSource<T[]>{ public static ListStabilized<T>(src: UIEventSource<T[]>): UIEventSource<T[]> {
const stable = new UIEventSource<T[]>(src.data) const stable = new UIEventSource<T[]>(src.data)
src.addCallback(list => { src.addCallback(list => {
if(list === undefined){ if (list === undefined) {
stable.setData(undefined) stable.setData(undefined)
return; return;
} }
const oldList = stable.data const oldList = stable.data
if(oldList === list){ if (oldList === list) {
return; return;
} }
if(oldList.length !== list.length){ if (oldList.length !== list.length) {
stable.setData(list); stable.setData(list);
return; return;
} }
for (let i = 0; i < list.length; i++) { for (let i = 0; i < list.length; i++) {
if(oldList[i] !== list[i]){ if (oldList[i] !== list[i]) {
stable.setData(list); stable.setData(list);
return; return;
} }
} }
// No actual changes, so we don't do anything // No actual changes, so we don't do anything
return; return;
}) })
return stable return stable
} }
/** /**
* Adds a callback * Adds a callback
* *
@ -190,21 +190,26 @@ export class UIEventSource<T> {
/** /**
* Monadic bind function * Monadic bind function
*/ */
public bind<X>(f: ((t: T) => UIEventSource<X>)): UIEventSource<X>{ public bind<X>(f: ((t: T) => UIEventSource<X>)): UIEventSource<X> {
const sink = new UIEventSource<X>( undefined ) const mapped = this.map(f)
const sink = new UIEventSource<X>(undefined)
const seenEventSources = new Set<UIEventSource<X>>(); const seenEventSources = new Set<UIEventSource<X>>();
this.addCallbackAndRun(data => { mapped.addCallbackAndRun(newEventSource => {
const eventSource = f(data)
if(eventSource === undefined){ if (newEventSource === undefined) {
sink.setData(undefined) sink.setData(undefined)
}else if(!seenEventSources.has(eventSource)){ } else if (!seenEventSources.has(newEventSource)) {
eventSource.addCallbackAndRun(mappedData => sink.setData(mappedData)) seenEventSources.add(newEventSource)
seenEventSources.add(eventSource) newEventSource.addCallbackAndRun(resultData => {
if (mapped.data === newEventSource) {
sink.setData(resultData);
}
})
} }
}) })
return sink; return sink;
} }
/** /**
* Monoidal map: * Monoidal map:

View file

@ -70,7 +70,10 @@ export default class TagRenderingQuestion extends Combine {
return TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options) 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<any>, private static GenerateFullQuestion(tags: UIEventSource<any>,
@ -163,6 +166,13 @@ export default class TagRenderingQuestion extends Combine {
applicableUnit: Unit, applicableUnit: Unit,
tagsSource: UIEventSource<any>) tagsSource: UIEventSource<any>)
: InputElement<TagsFilter> { : InputElement<TagsFilter> {
// 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<TagsFilter>[]; let inputEls: InputElement<TagsFilter>[];
@ -173,11 +183,11 @@ export default class TagRenderingQuestion extends Combine {
return undefined return undefined
} }
if (!ifNotsPresent) { if (!ifNotsPresent) {
return undefined return []
} }
if (configuration.multiAnswer) { if (configuration.multiAnswer) {
// The multianswer will do the ifnot configuration themself // The multianswer will do the ifnot configuration themself
return undefined return []
} }
const negativeMappings = [] 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) { if (applicableMappings.length < 8 || configuration.multiAnswer || hasImages || ifNotsPresent) {
inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i)));
inputEls = Utils.NoNull(inputEls); inputEls = Utils.NoNull(inputEls);
@ -226,7 +234,7 @@ export default class TagRenderingQuestion extends Combine {
} }
if (configuration.multiAnswer) { 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 { } else {
return new RadioButton(inputEls, {selectFirstAsDefault: false}) return new RadioButton(inputEls, {selectFirstAsDefault: false})
} }