More fixes

This commit is contained in:
pietervdvn 2021-06-16 21:23:03 +02:00
parent 2ae380f1a6
commit 9a73ae4c47
21 changed files with 203 additions and 148 deletions

View file

@ -7,8 +7,6 @@ import {Utils} from "../../Utils";
import {TagUtils} from "../../Logic/Tags/TagUtils";
import {And} from "../../Logic/Tags/And";
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
import {UIElement} from "../../UI/UIElement";
import {SubstitutedTranslation} from "../../UI/SubstitutedTranslation";
/***
* The parsed version of TagRenderingConfigJSON

View file

@ -2,7 +2,7 @@ import { Utils } from "../Utils";
export default class Constants {
public static vNumber = "0.8.0";
public static vNumber = "0.8.0-rc0";
// The user journey states thresholds when a new feature gets unlocked
public static userJourney = {

View file

@ -20,6 +20,7 @@ export class SubtleButton extends UIElement {
private static generateContent(imageUrl: string | BaseUIElement, messageT: string | BaseUIElement, linkTo: { url: string | UIEventSource<string>, newTab?: boolean } = undefined): BaseUIElement {
const message = Translations.W(messageT);
message
let img;
if ((imageUrl ?? "") === "") {
img = undefined;
@ -36,7 +37,7 @@ export class SubtleButton extends UIElement {
return new Combine([
image,
message?.SetClass("blcok ml-4 overflow-ellipsis"),
]).SetClass("flex group");
]).SetClass("flex group w-full");
}
@ -44,7 +45,7 @@ export class SubtleButton extends UIElement {
new Combine([
image,
message?.SetClass("block ml-4 overflow-ellipsis")
]).SetClass("flex group"),
]).SetClass("flex group w-full"),
linkTo.url,
linkTo.newTab ?? false
)

View file

@ -23,14 +23,14 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
const layoutToUse = State.state.layoutToUse.data;
super (
() => layoutToUse.title.Clone(),
() => FullWelcomePaneWithTabs.GenerateContents(layoutToUse, State.state.osmConnection.userDetails),
() => FullWelcomePaneWithTabs.GenerateContents(layoutToUse, State.state.osmConnection.userDetails, isShown),
"welcome" ,isShown
)
}
private static ConstructBaseTabs(layoutToUse: LayoutConfig): { header: string | BaseUIElement; content: BaseUIElement }[]{
private static ConstructBaseTabs(layoutToUse: LayoutConfig, isShown: UIEventSource<boolean>): { header: string | BaseUIElement; content: BaseUIElement }[]{
let welcome: BaseUIElement = new ThemeIntroductionPanel();
let welcome: BaseUIElement = new ThemeIntroductionPanel(isShown);
if (layoutToUse.id === personal.id) {
welcome = new PersonalLayersPanel();
}
@ -58,10 +58,10 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
return tabs;
}
private static GenerateContents(layoutToUse: LayoutConfig, userDetails: UIEventSource<UserDetails>) {
private static GenerateContents(layoutToUse: LayoutConfig, userDetails: UIEventSource<UserDetails>, isShown: UIEventSource<boolean>) {
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse)
const tabsWithAboutMc = [...FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse)]
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown)
const tabsWithAboutMc = [...FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown)]
tabsWithAboutMc.push({
header: Svg.help,
content: new Combine([Translations.t.general.aboutMapcomplete.Clone(), "<br/>Version " + Constants.vNumber])

View file

@ -4,26 +4,45 @@ import LanguagePicker from "../LanguagePicker";
import Translations from "../i18n/Translations";
import {VariableUiElement} from "../Base/VariableUIElement";
import Toggle from "../Input/Toggle";
import {SubtleButton} from "../Base/SubtleButton";
import Svg from "../../Svg";
import {UIEventSource} from "../../Logic/UIEventSource";
import {FixedUiElement} from "../Base/FixedUiElement";
export default class ThemeIntroductionPanel extends VariableUiElement {
constructor() {
constructor(isShown: UIEventSource<boolean>) {
const languagePicker =
new VariableUiElement(
State.state.layoutToUse.map(layout => LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone()))
)
;
const toTheMap = new SubtleButton(
new FixedUiElement(""),
Translations.t.general.openTheMap.Clone().SetClass("text-xl font-bold w-full text-center")
).onClick(() =>{
isShown.setData(false)
}).SetClass("only-on-mobile")
const plzLogIn =
Translations.t.general.loginWithOpenStreetMap
.Clone()
new SubtleButton(
Svg.osm_logo_ui(),
new Combine([Translations.t.general.loginWithOpenStreetMap
.Clone().SetClass("text-xl font-bold"),
Translations.t.general.loginOnlyNeededToEdit.Clone().SetClass("font-bold")]
).SetClass("flex flex-col text-center w-full")
)
.onClick(() => {
State.state.osmConnection.AttemptLogin()
});
const welcomeBack = Translations.t.general.welcomeBack.Clone();
const loginStatus =
new Toggle(
@ -40,6 +59,7 @@ export default class ThemeIntroductionPanel extends VariableUiElement {
super(State.state.layoutToUse.map (layout => new Combine([
layout.description.Clone(),
"<br/><br/>",
toTheMap,
loginStatus,
layout.descriptionTail.Clone(),
"<br/>",

View file

@ -13,7 +13,6 @@ export class SlideShow extends BaseUIElement {
protected InnerConstructElement(): HTMLElement {
const el = document.createElement("div")
el.classList.add("slic-carousel")
el.style.overflowX = "auto"
el.style.width = "min-content"
el.style.minWidth = "min-content"
@ -25,8 +24,9 @@ export class SlideShow extends BaseUIElement {
}
for (const element of elements ?? []) {
element.SetClass("block ml-1")
.SetStyle("width: 300px; max-height: var(--image-carousel-height); height: var(--image-carousel-height)")
element
.SetClass("block ml-1; bg-gray-200")
.SetStyle("min-width: 150; max-height: var(--image-carousel-height); min-height: var(--image-carousel-height)")
el.appendChild(element.ConstructElement())
}

View file

@ -7,77 +7,83 @@ import BaseUIElement from "../BaseUIElement";
* Supports multi-input
*/
export default class CheckBoxes extends InputElement<number[]> {
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _element : HTMLElement
private static _nextId = 0;
private readonly value: UIEventSource<number[]>
constructor(elements: BaseUIElement[], value =new UIEventSource<number[]>([])) {
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly value: UIEventSource<number[]>
private readonly _elements: BaseUIElement[];
constructor(elements: BaseUIElement[], value = new UIEventSource<number[]>([])) {
super();
this.value = value;
elements = Utils.NoNull(elements);
const el = document.createElement("form")
for (let i = 0; i < elements.length; i++) {
let inputI = elements[i];
const input = document.createElement("input")
const id = CheckBoxes._nextId
CheckBoxes._nextId ++;
input.id = "checkbox"+id
this._elements = Utils.NoNull(elements);
this.SetClass("flex flex-col")
input.type = "checkbox"
const label = document.createElement("label")
label.htmlFor = input.id
label.appendChild(inputI.ConstructElement())
value.addCallbackAndRun(selectedValues =>{
if(selectedValues === undefined){
return;
}
if(selectedValues.indexOf(i) >= 0){
input.checked = true;
}
})
input.onchange = () => {
const index = value.data.indexOf(i);
if(input.checked && index < 0){
value.data.push(i);
value.ping();
}else if(index >= 0){
value.data.splice(index,1);
value.ping();
}
}
el.appendChild(input)
el.appendChild(document.createElement("br"))
}
}
protected InnerConstructElement(): HTMLElement {
return this._element
}
IsValid(ts: number[]): boolean {
return ts !== undefined;
}
GetValue(): UIEventSource<number[]> {
return this.value;
}
protected InnerConstructElement(): HTMLElement {
const el = document.createElement("form")
const value = this.value;
const elements = this._elements;
for (let i = 0; i < elements.length; i++) {
let inputI = elements[i];
const input = document.createElement("input")
const id = CheckBoxes._nextId
CheckBoxes._nextId++;
input.id = "checkbox" + id
input.type = "checkbox"
input.classList.add("p-1","cursor-pointer","ml-3","pl-3")
const label = document.createElement("label")
label.htmlFor = input.id
label.appendChild(inputI.ConstructElement())
label.classList.add("block","w-full","p-2","cursor-pointer","bg-red")
const wrapper = document.createElement("span")
wrapper.classList.add("flex","w-full","border", "border-gray-400")
wrapper.appendChild(input)
wrapper.appendChild(label)
el.appendChild(wrapper)
value.addCallbackAndRun(selectedValues => {
if (selectedValues === undefined) {
return;
}
if (selectedValues.indexOf(i) >= 0) {
input.checked = true;
}
})
input.onchange = () => {
// Index = index in the list of already checked items
const index = value.data.indexOf(i);
if (input.checked && index < 0) {
value.data.push(i);
value.ping();
} else if (index >= 0) {
value.data.splice(index, 1);
value.ping();
}
}
}
return el;
}
}

View file

@ -18,6 +18,7 @@ export default class DirectionInput extends InputElement<string> {
this.value = value ?? new UIEventSource<string>(undefined);
}
protected InnerConstructElement(): HTMLElement {

View file

@ -7,12 +7,19 @@ export class RadioButton<T> extends InputElement<T> {
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly value: UIEventSource<T>;
private _elements: InputElement<T>[];
private readonly _element: HTMLElement;
private _selectFirstAsDefault: boolean;
constructor(elements: InputElement<T>[],
selectFirstAsDefault = true) {
super()
elements = Utils.NoNull(elements);
this._selectFirstAsDefault = selectFirstAsDefault;
this._elements = Utils.NoNull(elements);
this.value = new UIEventSource<T>(undefined)
}
protected InnerConstructElement(): HTMLElement {
const elements = this._elements;
const selectFirstAsDefault = this._selectFirstAsDefault;
const selectedElementIndex: UIEventSource<number> = new UIEventSource<number>(null);
const value =
UIEventSource.flatten(selectedElementIndex.map(
@ -22,6 +29,7 @@ export class RadioButton<T> extends InputElement<T> {
}
}
), elements.map(e => e?.GetValue()));
value.syncWith(this.value)
if(selectFirstAsDefault){
@ -61,7 +69,20 @@ export class RadioButton<T> extends InputElement<T> {
RadioButton._nextId++
const form = document.createElement("form")
this._element = form;
const inputs = []
value.addCallbackAndRun(
selected => {
let somethingChecked = false;
for (let i = 0; i < inputs.length; i++){
let input = inputs[i];
input.checked = !somethingChecked && elements[i].IsValid(selected);
somethingChecked = somethingChecked || input.checked
}
}
)
for (let i1 = 0; i1 < elements.length; i1++) {
let element = elements[i1];
const labelHtml = element.ConstructElement();
@ -73,6 +94,7 @@ export class RadioButton<T> extends InputElement<T> {
input.id = "radio" + groupId + "-" + i1;
input.name = groupId;
input.type = "radio"
input.classList.add("p-1","cursor-pointer","ml-2","pl-2","pr-0","m-0","ml-3")
input.onchange = () => {
if(input.checked){
@ -80,30 +102,26 @@ export class RadioButton<T> extends InputElement<T> {
}
}
value.addCallbackAndRun(
selected => input.checked = element.IsValid(selected)
)
inputs.push(input)
const label = document.createElement("label")
label.appendChild(labelHtml)
label.htmlFor = input.id;
label.classList.add("block","w-full","p-2","cursor-pointer","bg-red")
const block = document.createElement("div")
block.appendChild(input)
block.appendChild(label)
block.classList.add("flex","w-full","border", "rounded-full", "border-gray-400")
form.appendChild(block)
form.addEventListener("change", () => {
// TODO FIXME
}
);
}
this.value = value;
this._elements = elements;
this.SetClass("flex flex-col")
return form;
}
IsValid(t: T): boolean {
@ -120,9 +138,6 @@ export class RadioButton<T> extends InputElement<T> {
}
protected InnerConstructElement(): HTMLElement {
return this._element;
}
/*
public ShowValue(t: T): boolean {

View file

@ -67,10 +67,10 @@ export class TextField extends InputElement<string> {
this.value.addCallbackAndRun(value => {
if (!(value !== undefined && value !== null)) {
field["value"] = "";
return;
}
// @ts-ignore
field.value = value;
field["value"] = value;
if (self.IsValid(value)) {
self.RemoveClass("invalid")
} else {

View file

@ -8,15 +8,13 @@ import State from "../../State";
import Svg from "../../Svg";
import Toggle from "../Input/Toggle";
import BaseUIElement from "../BaseUIElement";
import {FixedUiElement} from "../Base/FixedUiElement";
export default class EditableTagRendering extends Toggle {
constructor(tags: UIEventSource<any>,
configuration: TagRenderingConfig) {
const editMode = new UIEventSource<boolean>(false);
configuration: TagRenderingConfig,
editMode = new UIEventSource<boolean>(false)
) {
const answer: BaseUIElement = new TagRenderingAnswer(tags, configuration)
answer.SetClass("w-full")
let rendering = answer;

View file

@ -26,8 +26,8 @@ export class SaveButton extends Toggle {
isSaveable
)
super(
save
, pleaseLogin,
save,
pleaseLogin,
osmConnection?.userDetails?.map(userDetails => userDetails.loggedIn) ?? new UIEventSource<any>(false)
)

View file

@ -71,7 +71,8 @@ export default class TagRenderingQuestion extends UIElement {
}
this._saveButton = new SaveButton(this._inputElement.GetValue(), State.state?.osmConnection)
this._saveButton = new SaveButton(this._inputElement.GetValue(),
State.state?.osmConnection)
.onClick(save)
@ -92,7 +93,7 @@ export default class TagRenderingQuestion extends UIElement {
return tags.asHumanString(true, true, self._tags.data);
}
)
).SetClass("block")
).SetClass("block break-all")
@ -156,7 +157,9 @@ export default class TagRenderingQuestion extends UIElement {
oppositeTags.push(notSelected);
}
tags.push(TagUtils.FlattenMultiAnswer(oppositeTags));
return TagUtils.FlattenMultiAnswer(tags);
const actualTags = TagUtils.FlattenMultiAnswer(tags);
console.log("Converted ", indices.join(","), "into", actualTags.asHumanString(false, false, {}), "with elems", elements)
return actualTags;
},
(tags: TagsFilter) => {
// {key --> values[]}

View file

@ -2,19 +2,14 @@
Contains tweaks for small screens
*/
.only-on-mobile {
display: none !important;
background-color: var(--background-color);
color: var(--foreground-color);
}
@media only screen and (max-width: 600px), only screen and (max-height: 600px) {
@media only screen and (min-width: 769px) {
.only-on-mobile {
display: unset !important;
background-color: var(--background-color);
color: var(--foreground-color);
display: none !important;
}
}
@media only screen and (max-width: 768px), only screen and (max-height: 768px) {
.hidden-on-mobile {
display: none !important;

View file

@ -1,18 +0,0 @@
.slick-carousel-content {
width: 300px;
max-height: var(--image-carousel-height);
display: block;
margin-left: 10px;
}
.slick-carousel-content img {
/**
Workaround to patch images within a slick carousel
*/
height: var(--image-carousel-height);
width: auto;
}

View file

@ -226,10 +226,6 @@ li::marker {
width: 100%;
}
.question form input[type="radio"] {
margin-right: 0.5em;
}
.invalid {
box-shadow: 0 0 10px #ff5353;
height: min-content;

View file

@ -70,7 +70,7 @@
<div id="bottom-right" class="absolute bottom-3 right-2 rounded-3xl z-above-map"></div>
<div id="centermessage"
class="clutter absolute h-24 left-24 right-24 top-56" style="z-index: 4000">
class="clutter absolute h-24 left-24 right-24 top-56 text-xl text-center" style="z-index: 4000">
Loading MapComplete, hang on...
</div>

View file

@ -29,6 +29,7 @@
},
"general": {
"loginWithOpenStreetMap": "Login with OpenStreetMap",
"loginOnlyNeededToEdit": "to make changes to the map",
"welcomeBack": "You are logged in, welcome back!",
"loginToStart": "Login to answer this question",
"testing":"Testing - changes won't be saved",
@ -39,6 +40,7 @@
"error": "Something went wrong…"
},
"returnToTheMap": "Return to the map",
"openTheMap": "Open the map",
"save": "Save",
"cancel": "Cancel",
"skip": "Skip this question",

View file

@ -1,5 +1,4 @@
import {Utils} from "../Utils";
Utils.runningFromConsole = true;
import SpecialVisualizations from "../UI/SpecialVisualizations";
import SimpleMetaTagger from "../Logic/SimpleMetaTagger";
@ -12,19 +11,19 @@ import {writeFileSync} from "fs";
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
import State from "../State";
import {QueryParameters} from "../Logic/Web/QueryParameters";
import Link from "../UI/Base/Link";
function WriteFile(filename, html: string | BaseUIElement, autogenSource: string): void {
function WriteFile(filename, html: string | BaseUIElement, autogenSource: string[]): void {
writeFileSync(filename, new Combine([Translations.W(html),
new Link("Generated from "+autogenSource, "../../../"+autogenSource)
"Generated from "+autogenSource.join(", ")
]).AsMarkdown());
}
WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage, "UI/SpecialVisualisations.ts")
WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage, ["UI/SpecialVisualisations.ts"])
WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"),
"SimpleMetaTagger and ExtraFunction")
WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(),"ValidatedTextField.ts");
["SimpleMetaTagger","ExtraFunction"])
WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(),["ValidatedTextField.ts"]);
new State(new LayoutConfig({
@ -51,7 +50,7 @@ new State(new LayoutConfig({
}))
QueryParameters.GetQueryParameter("layer-<layer-id>", "true", "Wether or not the layer with id <layer-id> is shown")
WriteFile("./Docs/URL_Parameters.md", QueryParameters.GenerateQueryParameterDocs(), "QueryParameters")
WriteFile("./Docs/URL_Parameters.md", QueryParameters.GenerateQueryParameterDocs(), ["QueryParameters"])
console.log("Generated docs")

View file

@ -3,7 +3,6 @@
<head>
<title>Small tests</title>
<link href="index.css" rel="stylesheet"/>
<link href="css/slideshow.css" rel="stylesheet"/>
<link href="css/tabbedComponent.css" rel="stylesheet"/>
<link href="css/openinghourstable.css" rel="stylesheet"/>
<link href="css/tagrendering.css" rel="stylesheet"/>

50
test.ts
View file

@ -1,10 +1,50 @@
import ValidatedTextField from "./UI/Input/ValidatedTextField";
import Combine from "./UI/Base/Combine";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
import {UIEventSource} from "./Logic/UIEventSource";
import TagRenderingConfig from "./Customizations/JSON/TagRenderingConfig";
import State from "./State";
import TagRenderingQuestion from "./UI/Popup/TagRenderingQuestion";
new Combine(ValidatedTextField.tpList.map(tp => {
const tf = ValidatedTextField.InputForType(tp.name);
return new Combine([tf, new VariableUiElement(tf.GetValue()).SetClass("alert")]);
})).AttachTo("maindiv")
function TestTagRendering(){
State.state = new State(undefined)
const tagsSource = new UIEventSource({
id:"node/1"
})
new TagRenderingQuestion(
tagsSource,
new TagRenderingConfig({
multiAnswer: false,
freeform: {
key:"valve"
},
question: "What valves are supported?",
render: "This pump supports {valve}",
mappings: [
{
if: "valve=dunlop",
then: "This pump supports dunlop"
},
{
if:"valve=shrader",
then:"shrader is supported",
}
],
}, undefined, "test")
).AttachTo("maindiv")
new VariableUiElement(tagsSource.map(tags => tags["valves"])).SetClass("alert").AttachTo("extradiv")
}
function TestAllInputMethods(){
new Combine(ValidatedTextField.tpList.map(tp => {
const tf = ValidatedTextField.InputForType(tp.name);
return new Combine([tf, new VariableUiElement(tf.GetValue()).SetClass("alert")]);
})).AttachTo("maindiv")
}
TestTagRendering()