forked from MapComplete/MapComplete
Improve typing of tag uploads, fix uploading of substituting tags
This commit is contained in:
parent
cbf5c5a748
commit
816670fe49
4 changed files with 82 additions and 44 deletions
|
@ -1,4 +1,6 @@
|
||||||
import {TagsFilter} from "./TagsFilter";
|
import {TagsFilter} from "./TagsFilter";
|
||||||
|
import {Tag} from "./Tag";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The substituting-tag uses the tags of a feature a variables and replaces them.
|
* The substituting-tag uses the tags of a feature a variables and replaces them.
|
||||||
|
@ -27,6 +29,13 @@ export default class SubstitutingTag implements TagsFilter {
|
||||||
return template.replace(/{.*}/g, "");
|
return template.replace(/{.*}/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asTag(currentProperties: Record<string, string>){
|
||||||
|
if(this._invert){
|
||||||
|
throw "Cannot convert an inverted substituting tag"
|
||||||
|
}
|
||||||
|
return new Tag(this._key, Utils.SubstituteKeys(this._value, currentProperties))
|
||||||
|
}
|
||||||
|
|
||||||
asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
|
asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
|
||||||
return this._key + (this._invert ? '!' : '') + "=" + SubstitutingTag.substituteString(this._value, properties);
|
return this._key + (this._invert ? '!' : '') + "=" + SubstitutingTag.substituteString(this._value, properties);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {isRegExp} from "util";
|
||||||
import * as key_counts from "../../assets/key_totals.json"
|
import * as key_counts from "../../assets/key_totals.json"
|
||||||
|
|
||||||
type Tags = Record<string, string>
|
type Tags = Record<string, string>
|
||||||
|
export type UploadableTag = Tag | SubstitutingTag | And
|
||||||
|
|
||||||
export class TagUtils {
|
export class TagUtils {
|
||||||
private static keyCounts: { keys: any, tags: any } = key_counts["default"] ?? key_counts
|
private static keyCounts: { keys: any, tags: any } = key_counts["default"] ?? key_counts
|
||||||
|
@ -58,7 +59,7 @@ export class TagUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SplitKeys(tagsFilters: TagsFilter[]): Record<string, string[]> {
|
static SplitKeys(tagsFilters: UploadableTag[]): Record<string, string[]> {
|
||||||
return <any>this.SplitKeysRegex(tagsFilters, false);
|
return <any>this.SplitKeysRegex(tagsFilters, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ export class TagUtils {
|
||||||
*
|
*
|
||||||
* TagUtils.SplitKeysRegex([new Tag("isced:level", "bachelor; master")], true) // => {"isced:level": ["bachelor","master"]}
|
* TagUtils.SplitKeysRegex([new Tag("isced:level", "bachelor; master")], true) // => {"isced:level": ["bachelor","master"]}
|
||||||
*/
|
*/
|
||||||
static SplitKeysRegex(tagsFilters: TagsFilter[], allowRegex: boolean): Record<string, (string | RegexTag)[]> {
|
static SplitKeysRegex(tagsFilters: UploadableTag[], allowRegex: boolean): Record<string, (string | RegexTag)[]> {
|
||||||
const keyValues: Record<string, (string | RegexTag)[]> = {}
|
const keyValues: Record<string, (string | RegexTag)[]> = {}
|
||||||
tagsFilters = [...tagsFilters] // copy all, use as queue
|
tagsFilters = [...tagsFilters] // copy all, use as queue
|
||||||
while (tagsFilters.length > 0) {
|
while (tagsFilters.length > 0) {
|
||||||
|
@ -78,7 +79,7 @@ export class TagUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tagsFilter instanceof And) {
|
if (tagsFilter instanceof And) {
|
||||||
tagsFilters.push(...tagsFilter.and);
|
tagsFilters.push(...<UploadableTag[]>tagsFilter.and);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,10 +113,28 @@ export class TagUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Flattens an 'uploadableTag' and replaces all 'SubstitutingTags' into normal tags
|
||||||
|
*/
|
||||||
|
static FlattenAnd(tagFilters: UploadableTag, currentProperties: Record<string, string>): Tag[]{
|
||||||
|
const tags : Tag[] = []
|
||||||
|
tagFilters.visit((tf: UploadableTag) => {
|
||||||
|
if(tf instanceof Tag){
|
||||||
|
tags.push(tf)
|
||||||
|
}
|
||||||
|
if(tf instanceof SubstitutingTag){
|
||||||
|
tags.push(tf.asTag(currentProperties))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
* Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set.
|
* Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set.
|
||||||
* E.g:
|
* E.g:
|
||||||
*
|
*
|
||||||
* const tag = TagUtils.Tag({"and": [
|
* const tag = TagUtils.ParseUploadableTag({"and": [
|
||||||
* {
|
* {
|
||||||
* and: [ "x=a", "y=0;1"],
|
* and: [ "x=a", "y=0;1"],
|
||||||
* },
|
* },
|
||||||
|
@ -131,13 +150,13 @@ export class TagUtils {
|
||||||
* TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => new And([new Tag("x","y"), new Tag("a","b")])
|
* TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => new And([new Tag("x","y"), new Tag("a","b")])
|
||||||
* TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => new And([new Tag("x","")])
|
* TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => new And([new Tag("x","")])
|
||||||
*/
|
*/
|
||||||
static FlattenMultiAnswer(tagsFilters: TagsFilter[]): And {
|
static FlattenMultiAnswer(tagsFilters: UploadableTag[]): And {
|
||||||
if (tagsFilters === undefined) {
|
if (tagsFilters === undefined) {
|
||||||
return new And([]);
|
return new And([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let keyValues = TagUtils.SplitKeys(tagsFilters);
|
let keyValues = TagUtils.SplitKeys(tagsFilters);
|
||||||
const and: TagsFilter[] = []
|
const and: UploadableTag[] = []
|
||||||
for (const key in keyValues) {
|
for (const key in keyValues) {
|
||||||
const values = Utils.Dedup(keyValues[key]).filter(v => v !== "")
|
const values = Utils.Dedup(keyValues[key]).filter(v => v !== "")
|
||||||
values.sort()
|
values.sort()
|
||||||
|
@ -157,7 +176,7 @@ export class TagUtils {
|
||||||
* // should match with a space too
|
* // should match with a space too
|
||||||
* TagUtils.MatchesMultiAnswer(new Tag("isced:level","master"), {"isced:level":"bachelor; master"}) // => true
|
* TagUtils.MatchesMultiAnswer(new Tag("isced:level","master"), {"isced:level":"bachelor; master"}) // => true
|
||||||
*/
|
*/
|
||||||
static MatchesMultiAnswer(tag: TagsFilter, properties: Tags): boolean {
|
static MatchesMultiAnswer(tag: UploadableTag, properties: Tags): boolean {
|
||||||
const splitted = TagUtils.SplitKeysRegex([tag], true);
|
const splitted = TagUtils.SplitKeysRegex([tag], true);
|
||||||
for (const splitKey in splitted) {
|
for (const splitKey in splitted) {
|
||||||
const neededValues = splitted[splitKey];
|
const neededValues = splitted[splitKey];
|
||||||
|
@ -250,13 +269,32 @@ export class TagUtils {
|
||||||
*/
|
*/
|
||||||
public static Tag(json: TagConfigJson, context: string = ""): TagsFilter {
|
public static Tag(json: TagConfigJson, context: string = ""): TagsFilter {
|
||||||
try {
|
try {
|
||||||
return this.TagUnsafe(json, context);
|
return this.ParseTagUnsafe(json, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Could not parse tag", json, "in context", context, "due to ", e)
|
console.error("Could not parse tag", json, "in context", context, "due to ", e)
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParseUploadableTag(json: TagConfigJson, context: string = ""): UploadableTag {
|
||||||
|
const t = this.Tag(json, context);
|
||||||
|
|
||||||
|
t.visit((t : TagsFilter)=> {
|
||||||
|
if( t instanceof And){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(t instanceof Tag){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(t instanceof SubstitutingTag){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw `Error at ${context}: detected a non-uploadable tag at a location where this is not supported: ${t.asHumanString(false, false, {})}`
|
||||||
|
})
|
||||||
|
|
||||||
|
return <any> t
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as `.Tag`, except that this will return undefined if the json is undefined
|
* Same as `.Tag`, except that this will return undefined if the json is undefined
|
||||||
* @param json
|
* @param json
|
||||||
|
@ -317,11 +355,11 @@ export class TagUtils {
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const [_, key, invert, modifier, value] = match;
|
const [ , key, invert, modifier, value] = match;
|
||||||
return {key, value, invert: invert == "!", modifier: (modifier == "i~" ? "i" : "")};
|
return {key, value, invert: invert == "!", modifier: (modifier == "i~" ? "i" : "")};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TagUnsafe(json: TagConfigJson, context: string = ""): TagsFilter {
|
private static ParseTagUnsafe(json: TagConfigJson, context: string = ""): TagsFilter {
|
||||||
|
|
||||||
if (json === undefined) {
|
if (json === undefined) {
|
||||||
throw new Error(`Error while parsing a tag: 'json' is undefined in ${context}. Make sure all the tags are defined and at least one tag is present in a complex expression`)
|
throw new Error(`Error while parsing a tag: 'json' is undefined in ${context}. Make sure all the tags are defined and at least one tag is present in a complex expression`)
|
||||||
|
@ -492,17 +530,6 @@ export class TagUtils {
|
||||||
}
|
}
|
||||||
return " (" + joined + ") "
|
return " (" + joined + ") "
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExtractSimpleTags(tf: TagsFilter) : Tag[] {
|
|
||||||
const result: Tag[] = []
|
|
||||||
tf.visit(t => {
|
|
||||||
if(t instanceof Tag){
|
|
||||||
result.push(t)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns 'true' is opposite tags are detected.
|
* Returns 'true' is opposite tags are detected.
|
||||||
* Note that this method will never work perfectly
|
* Note that this method will never work perfectly
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {Translation, TypedTranslation} from "../../UI/i18n/Translation";
|
import {Translation, TypedTranslation} from "../../UI/i18n/Translation";
|
||||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||||
import Translations from "../../UI/i18n/Translations";
|
import Translations from "../../UI/i18n/Translations";
|
||||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
import {TagUtils, UploadableTag} from "../../Logic/Tags/TagUtils";
|
||||||
import {And} from "../../Logic/Tags/And";
|
import {And} from "../../Logic/Tags/And";
|
||||||
import ValidatedTextField from "../../UI/Input/ValidatedTextField";
|
import ValidatedTextField from "../../UI/Input/ValidatedTextField";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
|
@ -16,8 +16,8 @@ import {FixedUiElement} from "../../UI/Base/FixedUiElement";
|
||||||
import {Paragraph} from "../../UI/Base/Paragraph";
|
import {Paragraph} from "../../UI/Base/Paragraph";
|
||||||
|
|
||||||
export interface Mapping {
|
export interface Mapping {
|
||||||
readonly if: TagsFilter,
|
readonly if: UploadableTag,
|
||||||
readonly ifnot?: TagsFilter,
|
readonly ifnot?: UploadableTag,
|
||||||
readonly then: TypedTranslation<object>,
|
readonly then: TypedTranslation<object>,
|
||||||
readonly icon: string,
|
readonly icon: string,
|
||||||
readonly iconClass: string | "small" | "medium" | "large" | "small-height" | "medium-height" | "large-height",
|
readonly iconClass: string | "small" | "medium" | "large" | "small-height" | "medium-height" | "large-height",
|
||||||
|
@ -46,7 +46,7 @@ export default class TagRenderingConfig {
|
||||||
readonly key: string,
|
readonly key: string,
|
||||||
readonly type: string,
|
readonly type: string,
|
||||||
readonly placeholder: Translation,
|
readonly placeholder: Translation,
|
||||||
readonly addExtraTags: TagsFilter[];
|
readonly addExtraTags: UploadableTag[];
|
||||||
readonly inline: boolean,
|
readonly inline: boolean,
|
||||||
readonly default?: string,
|
readonly default?: string,
|
||||||
readonly helperArgs?: (string | number | boolean)[]
|
readonly helperArgs?: (string | number | boolean)[]
|
||||||
|
@ -138,7 +138,7 @@ export default class TagRenderingConfig {
|
||||||
type,
|
type,
|
||||||
placeholder,
|
placeholder,
|
||||||
addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
|
addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
|
||||||
TagUtils.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
|
TagUtils.ParseUploadableTag(tg, `${context}.extratag[${i}]`)) ?? [],
|
||||||
inline: json.freeform.inline ?? false,
|
inline: json.freeform.inline ?? false,
|
||||||
default: json.freeform.default,
|
default: json.freeform.default,
|
||||||
helperArgs: json.freeform.helperArgs
|
helperArgs: json.freeform.helperArgs
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {SubstitutedTranslation} from "../SubstitutedTranslation";
|
||||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||||
import {Tag} from "../../Logic/Tags/Tag";
|
import {Tag} from "../../Logic/Tags/Tag";
|
||||||
import {And} from "../../Logic/Tags/And";
|
import {And} from "../../Logic/Tags/And";
|
||||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
import {TagUtils, UploadableTag} from "../../Logic/Tags/TagUtils";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import {DropDown} from "../Input/DropDown";
|
import {DropDown} from "../Input/DropDown";
|
||||||
import InputElementWrapper from "../Input/InputElementWrapper";
|
import InputElementWrapper from "../Input/InputElementWrapper";
|
||||||
|
@ -82,14 +82,16 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
|
|
||||||
|
|
||||||
const feedback = new UIEventSource<Translation>(undefined)
|
const feedback = new UIEventSource<Translation>(undefined)
|
||||||
const inputElement: ReadonlyInputElement<TagsFilter> =
|
const inputElement: ReadonlyInputElement<UploadableTag> =
|
||||||
new VariableInputElement(applicableMappingsSrc.map(applicableMappings => {
|
new VariableInputElement(applicableMappingsSrc.map(applicableMappings => {
|
||||||
return TagRenderingQuestion.GenerateInputElement(state, configuration, applicableMappings, applicableUnit, tags, feedback)
|
return TagRenderingQuestion.GenerateInputElement(state, configuration, applicableMappings, applicableUnit, tags, feedback)
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
const save = () => {
|
const save = () => {
|
||||||
const selection = TagUtils.FlattenMultiAnswer([inputElement.GetValue().data]);
|
|
||||||
|
|
||||||
|
const selection = TagUtils.FlattenMultiAnswer(TagUtils.FlattenAnd( inputElement.GetValue().data, tags.data));
|
||||||
if (selection) {
|
if (selection) {
|
||||||
(state?.changes)
|
(state?.changes)
|
||||||
.applyAction(new ChangeTagAction(
|
.applyAction(new ChangeTagAction(
|
||||||
|
@ -147,11 +149,11 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
applicableUnit: Unit,
|
applicableUnit: Unit,
|
||||||
tagsSource: UIEventSource<any>,
|
tagsSource: UIEventSource<any>,
|
||||||
feedback: UIEventSource<Translation>
|
feedback: UIEventSource<Translation>
|
||||||
): ReadonlyInputElement<TagsFilter> {
|
): ReadonlyInputElement<UploadableTag> {
|
||||||
|
|
||||||
|
|
||||||
const hasImages = applicableMappings.findIndex(mapping => mapping.icon !== undefined) >= 0
|
const hasImages = applicableMappings.findIndex(mapping => mapping.icon !== undefined) >= 0
|
||||||
let inputEls: InputElement<TagsFilter>[];
|
let inputEls: InputElement<UploadableTag>[];
|
||||||
|
|
||||||
const ifNotsPresent = applicableMappings.some(mapping => mapping.ifnot !== undefined)
|
const ifNotsPresent = applicableMappings.some(mapping => mapping.ifnot !== undefined)
|
||||||
|
|
||||||
|
@ -166,7 +168,7 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
// FreeForm input will be undefined if not present; will already contain a special input element if applicable
|
// FreeForm input will be undefined if not present; will already contain a special input element if applicable
|
||||||
const ff = TagRenderingQuestion.GenerateFreeform(state, configuration, applicableUnit, tagsSource, feedback);
|
const ff = TagRenderingQuestion.GenerateFreeform(state, configuration, applicableUnit, tagsSource, feedback);
|
||||||
|
|
||||||
function allIfNotsExcept(excludeIndex: number): TagsFilter[] {
|
function allIfNotsExcept(excludeIndex: number): UploadableTag[] {
|
||||||
if (configuration.mappings === undefined || configuration.mappings.length === 0) {
|
if (configuration.mappings === undefined || configuration.mappings.length === 0) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -196,7 +198,7 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(state, tagsSource, mapping, allIfNotsExcept(i)));
|
inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(state, tagsSource, mapping, allIfNotsExcept(i)));
|
||||||
inputEls = Utils.NoNull(inputEls);
|
inputEls = Utils.NoNull(inputEls);
|
||||||
} else {
|
} else {
|
||||||
const dropdown: InputElement<TagsFilter> = new DropDown("",
|
const dropdown: InputElement<UploadableTag> = new DropDown("",
|
||||||
applicableMappings.map((mapping, i) => {
|
applicableMappings.map((mapping, i) => {
|
||||||
return {
|
return {
|
||||||
value: new And([mapping.if, ...allIfNotsExcept(i)]),
|
value: new And([mapping.if, ...allIfNotsExcept(i)]),
|
||||||
|
@ -327,7 +329,7 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
tagsSource: UIEventSource<OsmTags>,
|
tagsSource: UIEventSource<OsmTags>,
|
||||||
options?: {
|
options?: {
|
||||||
search: UIEventSource<string>
|
search: UIEventSource<string>
|
||||||
}): InputElement<TagsFilter> {
|
}): InputElement<UploadableTag> {
|
||||||
|
|
||||||
|
|
||||||
const values = TagRenderingQuestion.MappingToPillValue(applicableMappings, tagsSource, state)
|
const values = TagRenderingQuestion.MappingToPillValue(applicableMappings, tagsSource, state)
|
||||||
|
@ -416,7 +418,6 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
return mapping.ifnot
|
return mapping.ifnot
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
console.log("Got tags", tfs)
|
|
||||||
return new And(tfs);
|
return new And(tfs);
|
||||||
},
|
},
|
||||||
(tf) => {
|
(tf) => {
|
||||||
|
@ -438,10 +439,10 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
|
|
||||||
private static GenerateMultiAnswer(
|
private static GenerateMultiAnswer(
|
||||||
configuration: TagRenderingConfig,
|
configuration: TagRenderingConfig,
|
||||||
elements: InputElement<TagsFilter>[], freeformField: InputElement<TagsFilter>, ifNotSelected: TagsFilter[]): InputElement<TagsFilter> {
|
elements: InputElement<UploadableTag>[], freeformField: InputElement<UploadableTag>, ifNotSelected: UploadableTag[]): InputElement<UploadableTag> {
|
||||||
const checkBoxes = new CheckBoxes(elements);
|
const checkBoxes = new CheckBoxes(elements);
|
||||||
|
|
||||||
const inputEl = new InputElementMap<number[], TagsFilter>(
|
const inputEl = new InputElementMap<number[], UploadableTag>(
|
||||||
checkBoxes,
|
checkBoxes,
|
||||||
(t0, t1) => {
|
(t0, t1) => {
|
||||||
return t0?.shadows(t1) ?? false
|
return t0?.shadows(t1) ?? false
|
||||||
|
@ -450,8 +451,8 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
if (indices.length === 0) {
|
if (indices.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const tags: TagsFilter[] = indices.map(i => elements[i].GetValue().data);
|
const tags: UploadableTag[] = indices.map(i => elements[i].GetValue().data);
|
||||||
const oppositeTags: TagsFilter[] = [];
|
const oppositeTags: UploadableTag[] = [];
|
||||||
for (let i = 0; i < ifNotSelected.length; i++) {
|
for (let i = 0; i < ifNotSelected.length; i++) {
|
||||||
if (indices.indexOf(i) >= 0) {
|
if (indices.indexOf(i) >= 0) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -465,8 +466,9 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
tags.push(TagUtils.FlattenMultiAnswer(oppositeTags));
|
tags.push(TagUtils.FlattenMultiAnswer(oppositeTags));
|
||||||
return TagUtils.FlattenMultiAnswer(tags);
|
return TagUtils.FlattenMultiAnswer(tags);
|
||||||
},
|
},
|
||||||
(tags: TagsFilter) => {
|
(tags: UploadableTag) => {
|
||||||
// {key --> values[]}
|
// {key --> values[]}
|
||||||
|
|
||||||
const presentTags = TagUtils.SplitKeys([tags]);
|
const presentTags = TagUtils.SplitKeys([tags]);
|
||||||
const indices: number[] = []
|
const indices: number[] = []
|
||||||
// We also collect the values that have to be added to the freeform field
|
// We also collect the values that have to be added to the freeform field
|
||||||
|
@ -546,9 +548,9 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
private static GenerateMappingElement(
|
private static GenerateMappingElement(
|
||||||
state,
|
state,
|
||||||
tagsSource: UIEventSource<any>,
|
tagsSource: UIEventSource<any>,
|
||||||
mapping: Mapping, ifNot?: TagsFilter[]): InputElement<TagsFilter> {
|
mapping: Mapping, ifNot?: UploadableTag[]): InputElement<UploadableTag> {
|
||||||
|
|
||||||
let tagging: TagsFilter = mapping.if;
|
let tagging: UploadableTag = mapping.if;
|
||||||
if (ifNot !== undefined) {
|
if (ifNot !== undefined) {
|
||||||
tagging = new And([mapping.if, ...ifNot])
|
tagging = new And([mapping.if, ...ifNot])
|
||||||
}
|
}
|
||||||
|
@ -572,7 +574,7 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GenerateFreeform(state: FeaturePipelineState, configuration: TagRenderingConfig, applicableUnit: Unit, tags: UIEventSource<any>, feedback: UIEventSource<Translation>)
|
private static GenerateFreeform(state: FeaturePipelineState, configuration: TagRenderingConfig, applicableUnit: Unit, tags: UIEventSource<any>, feedback: UIEventSource<Translation>)
|
||||||
: InputElement<TagsFilter> {
|
: InputElement<UploadableTag> {
|
||||||
const freeform = configuration.freeform;
|
const freeform = configuration.freeform;
|
||||||
if (freeform === undefined) {
|
if (freeform === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -639,7 +641,7 @@ export default class TagRenderingQuestion extends Combine {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let inputTagsFilter: InputElement<TagsFilter> = new InputElementMap(
|
let inputTagsFilter: InputElement<UploadableTag> = new InputElementMap(
|
||||||
input, (a, b) => a === b || (a?.shadows(b) ?? false),
|
input, (a, b) => a === b || (a?.shadows(b) ?? false),
|
||||||
pickString, toString
|
pickString, toString
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue