Add support for units to clean up tags when they enter mapcomplete; add example of this usage in the climbing theme, add climbing theme title icons with length and needed number of carabiners

This commit is contained in:
Pieter Vander Vennet 2021-06-22 03:16:45 +02:00
parent 89f6f606c8
commit 966fcda8d1
20 changed files with 302 additions and 111 deletions

View file

@ -8,11 +8,13 @@ import State from "../../State";
import Svg from "../../Svg";
import Toggle from "../Input/Toggle";
import BaseUIElement from "../BaseUIElement";
import {Unit} from "../../Customizations/JSON/Denomination";
export default class EditableTagRendering extends Toggle {
constructor(tags: UIEventSource<any>,
configuration: TagRenderingConfig,
units: Unit [],
editMode = new UIEventSource<boolean>(false)
) {
const answer: BaseUIElement = new TagRenderingAnswer(tags, configuration)
@ -41,7 +43,7 @@ export default class EditableTagRendering extends Toggle {
editMode.setData(false)
});
const question = new TagRenderingQuestion(tags, configuration,
const question = new TagRenderingQuestion(tags, configuration,units,
() => {
editMode.setData(false)
},

View file

@ -17,7 +17,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
public constructor(
tags: UIEventSource<any>,
layerConfig: LayerConfig
layerConfig: LayerConfig,
) {
super(() => FeatureInfoBox.GenerateTitleBar(tags, layerConfig),
() => FeatureInfoBox.GenerateContent(tags, layerConfig),
@ -35,7 +35,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
.SetClass("break-words font-bold sm:p-0.5 md:p-1 sm:p-1.5 md:p-2");
const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon,
"block w-8 h-8 align-baseline box-content sm:p-0.5", "width: 2rem !important;")
"block w-8 h-8 align-baseline box-content sm:p-0.5")
))
.SetClass("flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2")
@ -49,7 +49,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
let questionBox: UIElement = undefined;
if (State.state.featureSwitchUserbadge.data) {
questionBox = new QuestionBox(tags, layerConfig.tagRenderings);
questionBox = new QuestionBox(tags, layerConfig.tagRenderings, layerConfig.units);
}
let questionBoxIsUsed = false;
@ -59,7 +59,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
questionBoxIsUsed = true;
return questionBox;
}
return new EditableTagRendering(tags, tr);
return new EditableTagRendering(tags, tr, layerConfig.units);
});
if (!questionBoxIsUsed) {
renderings.push(questionBox);

View file

@ -5,6 +5,8 @@ import TagRenderingQuestion from "./TagRenderingQuestion";
import Translations from "../i18n/Translations";
import State from "../../State";
import Combine from "../Base/Combine";
import BaseUIElement from "../BaseUIElement";
import {Unit} from "../../Customizations/JSON/Denomination";
/**
@ -14,12 +16,12 @@ export default class QuestionBox extends UIElement {
private readonly _tags: UIEventSource<any>;
private readonly _tagRenderings: TagRenderingConfig[];
private _tagRenderingQuestions: UIElement[];
private _tagRenderingQuestions: BaseUIElement[];
private _skippedQuestions: UIEventSource<number[]> = new UIEventSource<number[]>([])
private _skippedQuestionsButton: UIElement;
private _skippedQuestionsButton: BaseUIElement;
constructor(tags: UIEventSource<any>, tagRenderings: TagRenderingConfig[]) {
constructor(tags: UIEventSource<any>, tagRenderings: TagRenderingConfig[], units: Unit[]) {
super(tags);
this.ListenTo(this._skippedQuestions);
this._tags = tags;
@ -28,7 +30,7 @@ export default class QuestionBox extends UIElement {
.filter(tr => tr.question !== undefined)
.filter(tr => tr.question !== null);
this._tagRenderingQuestions = this._tagRenderings
.map((tagRendering, i) => new TagRenderingQuestion(this._tags, tagRendering,
.map((tagRendering, i) => new TagRenderingQuestion(this._tags, tagRendering,units,
() => {
// We save
self._skippedQuestions.ping();
@ -49,7 +51,7 @@ export default class QuestionBox extends UIElement {
}
InnerRender() {
const allQuestions : UIElement[] = []
const allQuestions : BaseUIElement[] = []
for (let i = 0; i < this._tagRenderingQuestions.length; i++) {
let tagRendering = this._tagRenderings[i];

View file

@ -24,6 +24,8 @@ import {And} from "../../Logic/Tags/And";
import {TagUtils} from "../../Logic/Tags/TagUtils";
import BaseUIElement from "../BaseUIElement";
import {DropDown} from "../Input/DropDown";
import {Unit} from "../../Customizations/JSON/Denomination";
import CombinedInputElement from "../Input/CombinedInputElement";
/**
* Shows the question element.
@ -38,14 +40,17 @@ export default class TagRenderingQuestion extends UIElement {
private _inputElement: InputElement<TagsFilter>;
private _cancelButton: BaseUIElement;
private _appliedTags: BaseUIElement;
private readonly _applicableUnit: Unit;
private _question: BaseUIElement;
constructor(tags: UIEventSource<any>,
configuration: TagRenderingConfig,
units: Unit[],
afterSave?: () => void,
cancelButton?: BaseUIElement
) {
super(tags);
this._applicableUnit = units.filter(unit => unit.isApplicableToKey(configuration.freeform?.key))[0];
this._tags = tags;
this._configuration = configuration;
this._cancelButton = cancelButton;
@ -114,9 +119,9 @@ export default class TagRenderingQuestion extends UIElement {
const self = this;
let inputEls: InputElement<TagsFilter>[];
const mappings = (this._configuration.mappings??[])
.filter( mapping => {
if(mapping.hideInAnswer === true){
const mappings = (this._configuration.mappings ?? [])
.filter(mapping => {
if (mapping.hideInAnswer === true) {
return false;
}
if (typeof (mapping.hideInAnswer) !== "boolean" && mapping.hideInAnswer.matchesProperties(this._tags.data)) {
@ -124,9 +129,9 @@ export default class TagRenderingQuestion extends UIElement {
}
return true;
})
let allIfNots: TagsFilter[] = Utils.NoNull(this._configuration.mappings?.map(m => m.ifnot) ?? [] );
let allIfNots: TagsFilter[] = Utils.NoNull(this._configuration.mappings?.map(m => m.ifnot) ?? []);
const ff = this.GenerateFreeform();
const hasImages = mappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0
@ -272,7 +277,7 @@ export default class TagRenderingQuestion extends UIElement {
then: Translation,
hideInAnswer: boolean | TagsFilter
}, ifNot?: TagsFilter[]): InputElement<TagsFilter> {
let tagging = mapping.if;
if (ifNot.length > 0) {
tagging = new And([tagging, ...ifNot])
@ -323,16 +328,41 @@ export default class TagRenderingQuestion extends UIElement {
return undefined;
}
const textField = ValidatedTextField.InputForType(this._configuration.freeform.type, {
let input: InputElement<string> = ValidatedTextField.InputForType(this._configuration.freeform.type, {
isValid: (str) => (str.length <= 255),
country: () => this._tags.data._country,
location: [this._tags.data._lat, this._tags.data._lon]
});
textField.GetValue().setData(this._tags.data[this._configuration.freeform.key]);
if (this._applicableUnit) {
// We need to apply a unit.
// This implies:
// We have to create a dropdown with applicable denominations, and fuse those values
const unit = this._applicableUnit
const unitDropDown = new DropDown("",
unit.denominations.map(denom => {
return {
shown: denom.human,
value: denom
}
})
)
unitDropDown.GetValue().setData(this._applicableUnit.defaultDenom)
unitDropDown.SetStyle("width: min-content")
input = new CombinedInputElement(
input,
unitDropDown,
(text, denom) => denom?.canonicalValue(text, true) ?? text,
(valueWithDenom: string) => unit.findDenomination(valueWithDenom)
).SetClass("flex")
}
input.GetValue().setData(this._tags.data[this._configuration.freeform.key]);
return new InputElementMap(
textField, (a, b) => a === b || (a?.isEquivalent(b) ?? false),
input, (a, b) => a === b || (a?.isEquivalent(b) ?? false),
pickString, toString
);