Fixing too many bugs, cleaning up some old parts of the code
This commit is contained in:
parent
3d05999f85
commit
00a6611e1f
21 changed files with 706 additions and 436 deletions
|
@ -259,8 +259,14 @@ export class FromJSON {
|
|||
const iconSize = FromJSON.TagRenderingWithDefault(json.iconSize, "iconSize", "40,40,center");
|
||||
const color = FromJSON.TagRenderingWithDefault(json.color, "layercolor", "#0000ff");
|
||||
const width = FromJSON.TagRenderingWithDefault(json.width, "layerwidth", "10");
|
||||
const tagRenderings = json.tagRenderings?.map(FromJSON.TagRendering) ?? [];
|
||||
|
||||
|
||||
let tagRenderingDefs = json.tagRenderings ?? [];
|
||||
if (tagRenderingDefs.indexOf("images") < 0) {
|
||||
tagRenderingDefs = ["images", ...tagRenderingDefs];
|
||||
}
|
||||
let tagRenderings = tagRenderingDefs.map(FromJSON.TagRendering);
|
||||
|
||||
|
||||
const renderTags = {"id": "node/-1"}
|
||||
const presets: Preset[] = json?.presets?.map(preset => {
|
||||
return ({
|
||||
|
@ -294,7 +300,7 @@ export class FromJSON {
|
|||
|
||||
// the anchor is always set from the center of the point
|
||||
// x, y with x going right and y going down if the values are bigger
|
||||
const popupAnchor = [0, -iconAnchor[1]+3];
|
||||
const popupAnchor = [0, 3 - iconAnchor[1]];
|
||||
|
||||
return {
|
||||
color: color.GetContent(tags).txt,
|
||||
|
|
|
@ -105,7 +105,16 @@ export class UIEventSource<T>{
|
|||
});
|
||||
|
||||
return newSource;
|
||||
|
||||
}
|
||||
|
||||
public static Chronic(millis: number):UIEventSource<Date>{
|
||||
const source = new UIEventSource<Date>(undefined);
|
||||
function run() {
|
||||
source.setData(new Date());
|
||||
window.setTimeout(run, millis);
|
||||
}
|
||||
run();
|
||||
return source;
|
||||
|
||||
}
|
||||
|
||||
|
|
35
State.ts
35
State.ts
|
@ -23,16 +23,15 @@ export class State {
|
|||
// The singleton of the global state
|
||||
public static state: State;
|
||||
|
||||
public static vNumber = "0.0.7d Refactored";
|
||||
public static vNumber = "0.0.7e Fixing all the bugs";
|
||||
|
||||
// The user journey states thresholds when a new feature gets unlocked
|
||||
public static userJourney = {
|
||||
customLayoutUnlock: 50,
|
||||
themeGeneratorUnlock: 500,
|
||||
tagsVisibleAt: 200,
|
||||
tagsVisibleAndWikiLinked: 250
|
||||
|
||||
|
||||
personalLayoutUnlock: 20,
|
||||
tagsVisibleAt: 100,
|
||||
tagsVisibleAndWikiLinked: 150,
|
||||
themeGeneratorReadOnlyUnlock: 200,
|
||||
themeGeneratorFullUnlock: 500,
|
||||
};
|
||||
|
||||
public static runningFromConsole: boolean = false;
|
||||
|
@ -68,13 +67,6 @@ export class State {
|
|||
* The message that should be shown at the center of the screen
|
||||
*/
|
||||
public readonly centerMessage = new UIEventSource<string>("");
|
||||
|
||||
/**
|
||||
* The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource
|
||||
*/
|
||||
public readonly secondsTillChangesAreSaved = new UIEventSource<number>(0);
|
||||
|
||||
|
||||
/**
|
||||
This message is shown full screen on mobile devices
|
||||
*/
|
||||
|
@ -116,10 +108,6 @@ export class State {
|
|||
latlng: number,
|
||||
accuracy: number
|
||||
}> = new UIEventSource<{ latlng: number, accuracy: number }>(undefined);
|
||||
|
||||
/** After this many milliseconds without changes, saves are sent of to OSM
|
||||
*/
|
||||
public readonly saveTimeout = new UIEventSource<number>(30 * 1000);
|
||||
public layoutDefinition: string;
|
||||
public installedThemes: UIEventSource<{ layout: Layout; definition: string }[]>;
|
||||
|
||||
|
@ -259,16 +247,5 @@ export class State {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public GetFilteredLayerFor(id: string) : FilteredLayer{
|
||||
for (const flayer of this.filteredLayers.data) {
|
||||
if(flayer.layerDef.id === id){
|
||||
return flayer;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson";
|
|||
import Combine from "../Base/Combine";
|
||||
import {GenerateEmpty} from "./GenerateEmpty";
|
||||
import LayerPanelWithPreview from "./LayerPanelWithPreview";
|
||||
import {UserDetails} from "../../Logic/Osm/OsmConnection";
|
||||
|
||||
export default class AllLayersPanel extends UIElement {
|
||||
|
||||
|
@ -15,28 +16,29 @@ export default class AllLayersPanel extends UIElement {
|
|||
private readonly languages: UIEventSource<string[]>;
|
||||
|
||||
constructor(config: UIEventSource<LayoutConfigJson>,
|
||||
languages: UIEventSource<any>) {
|
||||
languages: UIEventSource<any>, userDetails: UserDetails) {
|
||||
super(undefined);
|
||||
this._config = config;
|
||||
this.languages = languages;
|
||||
|
||||
this.createPanels();
|
||||
this.createPanels(userDetails);
|
||||
const self = this;
|
||||
config.map<number>(config => config.layers.length).addCallback(() => self.createPanels());
|
||||
config.map<number>(config => config.layers.length).addCallback(() => self.createPanels(userDetails));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private createPanels() {
|
||||
private createPanels(userDetails: UserDetails) {
|
||||
const self = this;
|
||||
const tabs = [];
|
||||
|
||||
const layers = this._config.data.layers;
|
||||
for (let i = 0; i < layers.length; i++) {
|
||||
|
||||
|
||||
tabs.push({
|
||||
header: "<img src='./assets/bug.svg'>",
|
||||
content: new LayerPanelWithPreview(this._config, this.languages, i)});
|
||||
content: new LayerPanelWithPreview(this._config, this.languages, i, userDetails)
|
||||
});
|
||||
}
|
||||
tabs.push({
|
||||
header: "<img src='./assets/layersAdd.svg'>",
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import {OsmConnection, UserDetails} from "../../Logic/Osm/OsmConnection";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import SingleSetting from "./SingleSetting";
|
||||
import GeneralSettings from "./GeneralSettings";
|
||||
import Combine from "../Base/Combine";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import {TabbedComponent} from "../Base/TabbedComponent";
|
||||
import PageSplit from "../Base/PageSplit";
|
||||
import HelpText from "../../Customizations/HelpText";
|
||||
import AllLayersPanel from "./AllLayersPanel";
|
||||
import SharePanel from "./SharePanel";
|
||||
import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import {State} from "../../State";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import SavePanel from "./SavePanel";
|
||||
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
|
||||
|
||||
|
||||
export default class CustomGeneratorPanel extends UIElement {
|
||||
private mainPanel: UIElement;
|
||||
private loginButton: UIElement;
|
||||
|
||||
private connection: OsmConnection;
|
||||
|
||||
constructor(connection: OsmConnection, layout: LayoutConfigJson) {
|
||||
super(connection.userDetails);
|
||||
this.connection = connection;
|
||||
this.SetClass("main-tabs");
|
||||
this.loginButton = new SubtleButton("", "Login to create a custom theme").onClick(() => connection.AttemptLogin())
|
||||
const self = this;
|
||||
self.mainPanel = new FixedUiElement("Attempting to log in...");
|
||||
connection.OnLoggedIn(userDetails => {
|
||||
self.InitMainPanel(layout, userDetails, connection);
|
||||
self.Update();
|
||||
})
|
||||
}
|
||||
|
||||
private InitMainPanel(layout: LayoutConfigJson, userDetails: UserDetails, connection: OsmConnection) {
|
||||
const es = new UIEventSource(layout);
|
||||
const encoded = es.map(config => btoa(JSON.stringify(config)));
|
||||
encoded.addCallback(encoded => LocalStorageSource.Get("\"last-custom-theme\""))
|
||||
const liveUrl = encoded.map(encoded => `./index.html?userlayout=${es.data.id}#${encoded}`)
|
||||
const iframe = liveUrl.map(url => `<iframe src='${url}' width='100%' height='99%' style="box-sizing: border-box" title='Theme Preview'></iframe>`);
|
||||
const currentSetting = new UIEventSource<SingleSetting<any>>(undefined)
|
||||
const generalSettings = new GeneralSettings(es, currentSetting);
|
||||
const languages = generalSettings.languages;
|
||||
|
||||
const chronic = UIEventSource.Chronic(120 * 1000)
|
||||
.map(date => {
|
||||
if (es.data.id == undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (es.data.id === "") {
|
||||
return undefined;
|
||||
}
|
||||
const pref = connection.GetLongPreference("installed-theme-" + es.data.id);
|
||||
pref.setData(encoded.data);
|
||||
return date;
|
||||
});
|
||||
|
||||
const preview = new Combine([
|
||||
new VariableUiElement(iframe.stabilized(2500))
|
||||
]).SetClass("preview")
|
||||
this.mainPanel = new TabbedComponent([
|
||||
{
|
||||
header: "<img src='./assets/gear.svg'>",
|
||||
content:
|
||||
new PageSplit(
|
||||
generalSettings.SetStyle("width: 50vw;"),
|
||||
new Combine([
|
||||
new HelpText(currentSetting).SetStyle("height:calc(100% - 65vh); width: 100%; display:block; overflow-y: auto"),
|
||||
preview.SetStyle("height:65vh; width:100%; display:block")
|
||||
]).SetStyle("position:relative; width: 50%;")
|
||||
)
|
||||
},
|
||||
{
|
||||
header: "<img src='./assets/layers.svg'>",
|
||||
content: new AllLayersPanel(es, languages, userDetails)
|
||||
},
|
||||
{
|
||||
header: "<img src='./assets/floppy.svg'>",
|
||||
content: new SavePanel(this.connection, es, chronic)
|
||||
|
||||
},
|
||||
{
|
||||
header: "<img src='./assets/share.svg'>",
|
||||
content: new SharePanel(es, liveUrl)
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
InnerRender(): string {
|
||||
const ud = this.connection.userDetails.data;
|
||||
if (!ud.loggedIn) {
|
||||
return new Combine([
|
||||
"<h3>Not Logged in</h3>",
|
||||
"You need to be logged in in order to create a custom theme",
|
||||
this.loginButton
|
||||
]).Render();
|
||||
}
|
||||
if (ud.csCount <= State.userJourney.themeGeneratorReadOnlyUnlock) {
|
||||
return new Combine([
|
||||
"<h3>Too little experience/h3>",
|
||||
`Creating your own (readonly) themes can only be done if you have more then <b>${State.userJourney.themeGeneratorReadOnlyUnlock}</b> changesets made`,
|
||||
`Making a theme including survey options can be done at <b>${State.userJourney.themeGeneratorFullUnlock}</b> changesets`,
|
||||
this.loginButton
|
||||
]).Render();
|
||||
}
|
||||
return this.mainPanel.Render()
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,9 @@ import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConf
|
|||
import {MultiInput} from "../Input/MultiInput";
|
||||
import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson";
|
||||
import PresetInputPanel from "./PresetInputPanel";
|
||||
import {UserDetails} from "../../Logic/Osm/OsmConnection";
|
||||
import {State} from "../../State";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
|
||||
/**
|
||||
* Shows the configuration for a single layer
|
||||
|
@ -38,10 +41,11 @@ export default class LayerPanel extends UIElement {
|
|||
constructor(config: UIEventSource<LayoutConfigJson>,
|
||||
languages: UIEventSource<string[]>,
|
||||
index: number,
|
||||
currentlySelected: UIEventSource<SingleSetting<any>>) {
|
||||
currentlySelected: UIEventSource<SingleSetting<any>>,
|
||||
userDetails: UserDetails) {
|
||||
super();
|
||||
this._config = config;
|
||||
this.mapRendering = this.setupRenderOptions(config, languages, index, currentlySelected);
|
||||
this.mapRendering = this.setupRenderOptions(config, languages, index, currentlySelected, userDetails);
|
||||
|
||||
const actualDeleteButton = new SubtleButton(
|
||||
"./assets/delete.svg",
|
||||
|
@ -100,7 +104,7 @@ export default class LayerPanel extends UIElement {
|
|||
currentlySelected);
|
||||
const self = this;
|
||||
|
||||
const popupTitleRendering = new TagRenderingPanel(languages, currentlySelected, {
|
||||
const popupTitleRendering = new TagRenderingPanel(languages, currentlySelected, userDetails, {
|
||||
title: "Popup title",
|
||||
description: "This is the rendering shown as title in the popup for this element",
|
||||
disableQuestions: true
|
||||
|
@ -113,7 +117,7 @@ export default class LayerPanel extends UIElement {
|
|||
const tagRenderings = new MultiInput<TagRenderingConfigJson>("Add a tag rendering/question",
|
||||
() => ({}),
|
||||
() => {
|
||||
const tagPanel = new TagRenderingPanel(languages, currentlySelected)
|
||||
const tagPanel = new TagRenderingPanel(languages, currentlySelected, userDetails)
|
||||
self.registerTagRendering(tagPanel);
|
||||
return tagPanel;
|
||||
});
|
||||
|
@ -124,11 +128,16 @@ export default class LayerPanel extends UIElement {
|
|||
}
|
||||
)
|
||||
|
||||
const presetPanel = new MultiInput("Add a preset",
|
||||
() => ({tags: [], title: {}}),
|
||||
() => new PresetInputPanel(currentlySelected, languages));
|
||||
this.presetsPanel = presetPanel;
|
||||
new SingleSetting(config, presetPanel, ["layers", index, "presets"], "Presets", "")
|
||||
if (userDetails.csCount >= State.userJourney.themeGeneratorFullUnlock) {
|
||||
|
||||
const presetPanel = new MultiInput("Add a preset",
|
||||
() => ({tags: [], title: {}}),
|
||||
() => new PresetInputPanel(currentlySelected, languages));
|
||||
new SingleSetting(config, presetPanel, ["layers", index, "presets"], "Presets", "")
|
||||
this.presetsPanel = presetPanel;
|
||||
} else {
|
||||
this.presetsPanel = new FixedUiElement(`Creating a custom theme which also edits OSM is only unlocked after ${State.userJourney.themeGeneratorFullUnlock} changesets`).SetClass("alert");
|
||||
}
|
||||
|
||||
function loadTagRenderings() {
|
||||
const values = (config.data.layers[index] as LayerConfigJson).tagRenderings;
|
||||
|
@ -152,27 +161,29 @@ export default class LayerPanel extends UIElement {
|
|||
private setupRenderOptions(config: UIEventSource<LayoutConfigJson>,
|
||||
languages: UIEventSource<string[]>,
|
||||
index: number,
|
||||
currentlySelected: UIEventSource<SingleSetting<any>>): UIElement {
|
||||
currentlySelected: UIEventSource<SingleSetting<any>>,
|
||||
userDetails: UserDetails
|
||||
): UIElement {
|
||||
const iconSelect = new TagRenderingPanel(
|
||||
languages, currentlySelected,
|
||||
languages, currentlySelected, userDetails,
|
||||
{
|
||||
title: "Icon",
|
||||
description: "A visual representation for this layer and for the points on the map.",
|
||||
disableQuestions: true
|
||||
});
|
||||
const size = new TagRenderingPanel(languages, currentlySelected,
|
||||
const size = new TagRenderingPanel(languages, currentlySelected, userDetails,
|
||||
{
|
||||
title: "Icon Size",
|
||||
description: "The size of the icons on the map in pixels. Can vary based on the tagging",
|
||||
disableQuestions: true
|
||||
});
|
||||
const color = new TagRenderingPanel(languages, currentlySelected,
|
||||
const color = new TagRenderingPanel(languages, currentlySelected, userDetails,
|
||||
{
|
||||
title: "Way and area color",
|
||||
description: "The color or a shown way or area. Can vary based on the tagging",
|
||||
disableQuestions: true
|
||||
});
|
||||
const stroke = new TagRenderingPanel(languages, currentlySelected,
|
||||
const stroke = new TagRenderingPanel(languages, currentlySelected, userDetails,
|
||||
{
|
||||
title: "Stroke width",
|
||||
description: "The width of lines representing ways and the outline of areas. Can vary based on the tags",
|
||||
|
|
|
@ -8,16 +8,17 @@ import {FromJSON} from "../../Customizations/JSON/FromJSON";
|
|||
import Combine from "../Base/Combine";
|
||||
import PageSplit from "../Base/PageSplit";
|
||||
import TagRenderingPreview from "./TagRenderingPreview";
|
||||
import {UserDetails} from "../../Logic/Osm/OsmConnection";
|
||||
|
||||
|
||||
export default class LayerPanelWithPreview extends UIElement{
|
||||
private panel: UIElement;
|
||||
|
||||
constructor(config: UIEventSource<any>, languages: UIEventSource<string[]>, index: number) {
|
||||
constructor(config: UIEventSource<any>, languages: UIEventSource<string[]>, index: number, userDetails: UserDetails) {
|
||||
super();
|
||||
|
||||
const currentlySelected = new UIEventSource<(SingleSetting<any>)>(undefined);
|
||||
const layer = new LayerPanel(config, languages, index, currentlySelected);
|
||||
const layer = new LayerPanel(config, languages, index, currentlySelected, userDetails);
|
||||
const helpText = new HelpText(currentlySelected);
|
||||
|
||||
const previewTagInput = new MultiTagInput();
|
||||
|
|
49
UI/CustomGenerator/SavePanel.ts
Normal file
49
UI/CustomGenerator/SavePanel.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson";
|
||||
import Combine from "../Base/Combine";
|
||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
|
||||
export default class SavePanel extends UIElement {
|
||||
private json: UIElement;
|
||||
private lastSaveEl: UIElement;
|
||||
|
||||
constructor(
|
||||
connection: OsmConnection,
|
||||
config: UIEventSource<LayoutConfigJson>,
|
||||
chronic: UIEventSource<Date>) {
|
||||
super();
|
||||
|
||||
|
||||
|
||||
this.lastSaveEl = new VariableUiElement(chronic
|
||||
.map(date => {
|
||||
if (date === undefined) {
|
||||
return new FixedUiElement("Your theme will be saved automatically within two minutes... Click here to force saving").SetClass("alert").Render()
|
||||
}
|
||||
return "Your theme was last saved at " + date.toISOString()
|
||||
})).onClick(() => chronic.setData(new Date()));
|
||||
|
||||
this.json = new VariableUiElement(config.map(config => {
|
||||
return JSON.stringify(config, null, 2)
|
||||
.replace(/\n/g, "<br/>")
|
||||
.replace(/ /g, " ");
|
||||
}))
|
||||
.SetClass("literal-code");
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return new Combine([
|
||||
"<h3>Saving</h3>",
|
||||
this.lastSaveEl,
|
||||
"<h3>JSON configuration</h3>",
|
||||
"The url hash is actually no more then a BASE64-encoding of the below JSON. This json contains the full configuration of the theme.<br/>" +
|
||||
"This configuration is mainly useful for debugging",
|
||||
this.json
|
||||
]).SetClass("scrollable")
|
||||
.Render();
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,8 @@ import {MultiInput} from "../Input/MultiInput";
|
|||
import MappingInput from "./MappingInput";
|
||||
import {AndOrTagConfigJson} from "../../Customizations/JSON/TagConfigJson";
|
||||
import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConfigJson";
|
||||
import {UserDetails} from "../../Logic/Osm/OsmConnection";
|
||||
import {State} from "../../State";
|
||||
|
||||
export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> {
|
||||
|
||||
|
@ -24,6 +26,7 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
|
|||
|
||||
constructor(languages: UIEventSource<string[]>,
|
||||
currentlySelected: UIEventSource<SingleSetting<any>>,
|
||||
userDetails: UserDetails,
|
||||
options?: {
|
||||
title?: string,
|
||||
description?: string,
|
||||
|
@ -36,6 +39,10 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
|
|||
this.SetClass("min-height");
|
||||
|
||||
this.options = options ?? {};
|
||||
const questionsNotUnlocked = userDetails.csCount < State.userJourney.themeGeneratorFullUnlock;
|
||||
this.options.disableQuestions =
|
||||
(this.options.disableQuestions ?? false) &&
|
||||
questionsNotUnlocked;
|
||||
|
||||
this.intro = new Combine(["<h3>", options?.title ?? "TagRendering", "</h3>", options?.description ?? ""])
|
||||
this.IsImage = options?.isImage ?? false;
|
||||
|
@ -47,9 +54,9 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
|
|||
return new SingleSetting<any>(value, input, id, name, description);
|
||||
}
|
||||
|
||||
|
||||
const questionSettings = [
|
||||
|
||||
|
||||
setting(new MultiLingualTextFields(languages), "question", "Question", "If the key or mapping doesn't match, this question is asked"),
|
||||
|
||||
setting(new AndOrTagInput(), "condition", "Condition",
|
||||
|
@ -70,6 +77,8 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
|
|||
|
||||
const settings: (string | SingleSetting<any>)[] = [
|
||||
setting(new MultiLingualTextFields(languages), "render", "Value to show", " Renders this value. Note that <span class='literal-code'>{key}</span>-parts are substituted by the corresponding values of the element. If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value."),
|
||||
|
||||
questionsNotUnlocked ? `You need at least ${State.userJourney.themeGeneratorFullUnlock} changesets to unlock the 'question'-field and to use your theme to edit OSM data`: "",
|
||||
...(options?.disableQuestions ? [] : questionSettings),
|
||||
|
||||
"<h3>Mappings</h3>",
|
||||
|
|
|
@ -26,10 +26,10 @@ export class FeatureInfoBox extends UIElement {
|
|||
private readonly _title: UIElement;
|
||||
private readonly _osmLink: UIElement;
|
||||
private readonly _wikipedialink: UIElement;
|
||||
private _infoboxes: TagDependantUIElement[];
|
||||
private readonly _infoboxes: TagDependantUIElement[];
|
||||
|
||||
private _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone();
|
||||
private _someSkipped = Translations.t.general.skippedQuestions.Clone();
|
||||
private readonly _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone();
|
||||
private readonly _someSkipped = Translations.t.general.skippedQuestions.Clone();
|
||||
|
||||
constructor(
|
||||
feature: any,
|
||||
|
@ -53,7 +53,7 @@ export class FeatureInfoBox extends UIElement {
|
|||
tagRenderingOption.construct(deps));
|
||||
}
|
||||
function initTags() {
|
||||
self._infoboxes = []
|
||||
self._infoboxes.splice(0, self._infoboxes.length);
|
||||
for (const tagRenderingOption of elementsToShow) {
|
||||
self._infoboxes.push(
|
||||
tagRenderingOption.construct(deps));
|
||||
|
@ -101,10 +101,10 @@ export class FeatureInfoBox extends UIElement {
|
|||
|
||||
}
|
||||
|
||||
let questionsHtml = "";
|
||||
let questionElement: UIElement;
|
||||
|
||||
if (!State.state.osmConnection.userDetails.data.loggedIn) {
|
||||
let mostImportantQuestion;
|
||||
let mostImportantQuestion ;
|
||||
let score = -1000;
|
||||
for (const question of questions) {
|
||||
|
||||
|
@ -114,7 +114,7 @@ export class FeatureInfoBox extends UIElement {
|
|||
}
|
||||
}
|
||||
|
||||
questionsHtml = mostImportantQuestion?.Render() ?? "";
|
||||
questionElement = mostImportantQuestion;
|
||||
} else if (questions.length > 0) {
|
||||
// We select the most important question and render that one
|
||||
let mostImportantQuestion;
|
||||
|
@ -127,11 +127,11 @@ export class FeatureInfoBox extends UIElement {
|
|||
}
|
||||
}
|
||||
|
||||
questionsHtml = mostImportantQuestion?.Render() ?? "";
|
||||
questionElement = mostImportantQuestion;
|
||||
} else if (skippedQuestions == 1) {
|
||||
questionsHtml = this._oneSkipped.Render();
|
||||
questionElement = this._oneSkipped;
|
||||
} else if (skippedQuestions > 0) {
|
||||
questionsHtml = this._someSkipped.Render();
|
||||
questionElement = this._someSkipped;
|
||||
}
|
||||
|
||||
const title = new Combine([
|
||||
|
@ -140,12 +140,16 @@ export class FeatureInfoBox extends UIElement {
|
|||
this._osmLink]);
|
||||
|
||||
const infoboxcontents = new Combine(
|
||||
[ new VerticalCombine(info, "infobox-information "), questionsHtml]);
|
||||
[ new VerticalCombine(info, "infobox-information "), questionElement ?? ""]);
|
||||
|
||||
return "<div class='featureinfobox'>" +
|
||||
new Combine([
|
||||
"<div class='featureinfoboxtitle'>" + title.Render() + "</div>",
|
||||
"<div class='infoboxcontents'>" + infoboxcontents.Render() + "</div>"]).Render() + "</div>";
|
||||
"<div class='featureinfoboxtitle'>",
|
||||
title,
|
||||
"</div>",
|
||||
"<div class='infoboxcontents'>",
|
||||
infoboxcontents,
|
||||
"</div>"]).Render() + "</div>";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,24 +1,36 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import Translations from "./i18n/Translations";
|
||||
import {State} from "../State";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Combine from "./Base/Combine";
|
||||
|
||||
/**
|
||||
* Handles the full screen popup on mobile
|
||||
*/
|
||||
export class FullScreenMessageBoxHandler {
|
||||
export class FullScreenMessageBox extends UIElement {
|
||||
|
||||
private _uielement: UIEventSource<UIElement>;
|
||||
private _uielement: UIElement;
|
||||
private returnToTheMap: UIElement;
|
||||
|
||||
constructor(onClear: (() => void)) {
|
||||
this._uielement = State.state.fullScreenMessage;
|
||||
super(undefined);
|
||||
|
||||
const self = this;
|
||||
this._uielement.addCallback(function () {
|
||||
self.update();
|
||||
|
||||
State.state.fullScreenMessage.addCallback(uielement => {
|
||||
return self._uielement = uielement?.SetClass("messagesboxmobile-scroll")?.Activate();
|
||||
});
|
||||
|
||||
this.update();
|
||||
this._uielement = State.state.fullScreenMessage.data;
|
||||
this.ListenTo(State.state.fullScreenMessage);
|
||||
this.HideOnEmpty(true);
|
||||
|
||||
State.state.fullScreenMessage.addCallback(latestData => {
|
||||
if (latestData === undefined) {
|
||||
location.hash = "";
|
||||
} else {
|
||||
location.hash = "#element";
|
||||
}
|
||||
this.Update();
|
||||
})
|
||||
|
||||
if (window !== undefined) {
|
||||
window.onhashchange = function () {
|
||||
|
@ -30,36 +42,24 @@ export class FullScreenMessageBoxHandler {
|
|||
}
|
||||
}
|
||||
|
||||
Translations.t.general.returnToTheMap
|
||||
this.returnToTheMap = Translations.t.general.returnToTheMap.Clone()
|
||||
.SetClass("to-the-map")
|
||||
.onClick(() => {
|
||||
self._uielement.setData(undefined);
|
||||
console.log("Returning...")
|
||||
State.state.fullScreenMessage.setData(undefined);
|
||||
onClear();
|
||||
})
|
||||
.AttachTo("to-the-map");
|
||||
|
||||
self.Update();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
update() {
|
||||
const wrapper = document.getElementById("messagesboxmobilewrapper");
|
||||
const gen = this._uielement.data;
|
||||
if (gen === undefined) {
|
||||
wrapper.classList.add("hidden")
|
||||
if (location.hash !== "") {
|
||||
location.hash = ""
|
||||
}
|
||||
return;
|
||||
InnerRender(): string {
|
||||
if (this._uielement === undefined) {
|
||||
return "";
|
||||
}
|
||||
location.hash = "#element"
|
||||
wrapper.classList.remove("hidden");
|
||||
|
||||
gen
|
||||
?.HideOnEmpty(true)
|
||||
?.AttachTo("messagesboxmobile")
|
||||
?.Activate();
|
||||
|
||||
|
||||
return new Combine([this._uielement, this.returnToTheMap]).SetStyle("").Render();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -72,7 +72,7 @@ export class MoreScreen extends UIElement {
|
|||
|
||||
els.push(new VariableUiElement(
|
||||
State.state.osmConnection.userDetails.map(userDetails => {
|
||||
if (userDetails.csCount < State.userJourney.themeGeneratorUnlock) {
|
||||
if (userDetails.csCount < State.userJourney.themeGeneratorReadOnlyUnlock) {
|
||||
return tr.requestATheme.Render();
|
||||
}
|
||||
return new SubtleButton("./assets/pencil.svg", tr.createYourOwnTheme, {
|
||||
|
@ -86,7 +86,7 @@ export class MoreScreen extends UIElement {
|
|||
for (const k in AllKnownLayouts.allSets) {
|
||||
const layout : Layout = AllKnownLayouts.allSets[k];
|
||||
if (k === PersonalLayout.NAME) {
|
||||
if (State.state.osmConnection.userDetails.data.csCount < State.userJourney.customLayoutUnlock) {
|
||||
if (State.state.osmConnection.userDetails.data.csCount < State.userJourney.personalLayoutUnlock) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,6 @@ export class MoreScreen extends UIElement {
|
|||
|
||||
const customThemesNames = State.state.installedThemes.data ?? [];
|
||||
if (customThemesNames.length > 0) {
|
||||
console.log(customThemesNames)
|
||||
els.push(Translations.t.general.customThemeIntro)
|
||||
|
||||
for (const installed of State.state.installedThemes.data) {
|
||||
|
|
|
@ -68,7 +68,7 @@ export abstract class UIElement extends UIEventSource<string> {
|
|||
if (UIElement.runningFromConsole) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let element = document.getElementById(this.id);
|
||||
if (element === undefined || element === null) {
|
||||
// The element is not painted
|
||||
|
@ -99,7 +99,7 @@ export abstract class UIElement extends UIEventSource<string> {
|
|||
if (element.innerHTML === "") {
|
||||
element.parentElement.style.display = "none";
|
||||
} else {
|
||||
element.parentElement.style.display = undefined;
|
||||
element.parentElement.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ export abstract class UIElement extends UIEventSource<string> {
|
|||
|
||||
public abstract InnerRender(): string;
|
||||
|
||||
public Activate(): void {
|
||||
public Activate(): UIElement {
|
||||
for (const i in this) {
|
||||
const child = this[i];
|
||||
if (child instanceof UIElement) {
|
||||
|
@ -187,6 +187,7 @@ export abstract class UIElement extends UIEventSource<string> {
|
|||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
public IsEmpty(): boolean {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import Locale from "../UI/i18n/Locale";
|
||||
import {State} from "../State";
|
||||
import {Layout} from "../Customizations/Layout";
|
||||
import Translations from "./i18n/Translations";
|
||||
import Combine from "./Base/Combine";
|
||||
import {InitUiElements} from "../InitUiElements";
|
||||
|
@ -20,27 +19,18 @@ export class WelcomeMessage extends UIElement {
|
|||
super(State.state.osmConnection.userDetails);
|
||||
this.ListenTo(Locale.language);
|
||||
this.languagePicker = InitUiElements.CreateLanguagePicker(Translations.t.general.pickLanguage);
|
||||
const layout = State.state.layoutToUse.data;
|
||||
|
||||
function fromLayout(f: (layout: Layout) => (string | UIElement)): UIElement {
|
||||
return Translations.W(f(State.state.layoutToUse.data));
|
||||
}
|
||||
|
||||
this.description = fromLayout((layout) => layout.welcomeMessage);
|
||||
this.plzLogIn =
|
||||
fromLayout((layout) => layout.gettingStartedPlzLogin
|
||||
.onClick(() => {State.state.osmConnection.AttemptLogin()})
|
||||
);
|
||||
this.welcomeBack = fromLayout((layout) => layout.welcomeBackMessage);
|
||||
this.tail = fromLayout((layout) => layout.welcomeTail);
|
||||
this.description =Translations.W(layout.welcomeMessage);
|
||||
this.plzLogIn =
|
||||
Translations.W(layout.gettingStartedPlzLogin)
|
||||
.onClick(() => {
|
||||
State.state.osmConnection.AttemptLogin()
|
||||
});
|
||||
this.welcomeBack = Translations.W(layout.welcomeBackMessage);
|
||||
this.tail = Translations.W(layout.welcomeTail);
|
||||
}
|
||||
|
||||
protected InnerUpdate(htmlElement: HTMLElement) {
|
||||
super.InnerUpdate(htmlElement);
|
||||
console.log("Innerupdating welcome message")
|
||||
this.plzLogIn.Update();
|
||||
}
|
||||
|
||||
|
||||
InnerRender(): string {
|
||||
|
||||
let loginStatus = undefined;
|
||||
|
@ -52,7 +42,8 @@ export class WelcomeMessage extends UIElement {
|
|||
return new Combine([
|
||||
this.description,
|
||||
"<br/></br>",
|
||||
// TODO this button is broken - figure out why loginStatus,
|
||||
// TODO this button is broken - figure out why loginStatus,
|
||||
loginStatus,
|
||||
this.tail,
|
||||
"<br/>",
|
||||
this.languagePicker
|
||||
|
|
76
assets/arrow-left-smooth.svg
Normal file
76
assets/arrow-left-smooth.svg
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 26.458333 26.458334"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="arrow-left-smooth.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="19.262262"
|
||||
inkscape:cy="36.323203"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<sodipodi:guide
|
||||
position="13.229167,23.859748"
|
||||
orientation="1,0"
|
||||
id="guide815"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="14.944824,13.229167"
|
||||
orientation="0,1"
|
||||
id="guide817"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-270.54165)">
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:3.59588718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 20.139011,294.16029 c 0,0 -13.7995299,-7.53922 -13.8484369,-10.36091 -0.04891,-2.82169 13.8484369,-10.38607 13.8484369,-10.38607"
|
||||
id="path821"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
76
assets/arrow-right-smooth.svg
Normal file
76
assets/arrow-right-smooth.svg
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 26.458333 26.458334"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="arrow-right-smooth.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="-22.237738"
|
||||
inkscape:cy="36.323203"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<sodipodi:guide
|
||||
position="13.229167,23.859748"
|
||||
orientation="1,0"
|
||||
id="guide815"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="14.944824,13.229167"
|
||||
orientation="0,1"
|
||||
id="guide817"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-270.54165)">
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:3.59588718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6.3128214,273.41335 c 0,0 13.7995296,7.53922 13.8484366,10.36091 0.04891,2.82169 -13.8484366,10.38607 -13.8484366,10.38607"
|
||||
id="path821"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -12,7 +12,11 @@
|
|||
"amenity=bicycle_parking"
|
||||
]
|
||||
},
|
||||
"icon": "./assets/layers/bike_parking/parking.svg",
|
||||
"icon": {
|
||||
"render": {
|
||||
"en": "./assets/layers/bike_parking/parking.svg"
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"render": {
|
||||
"en": "50,50,bottom"
|
||||
|
|
|
@ -1,70 +1,25 @@
|
|||
import {UIEventSource} from "./Logic/UIEventSource";
|
||||
import SingleSetting from "./UI/CustomGenerator/SingleSetting";
|
||||
import Combine from "./UI/Base/Combine";
|
||||
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
||||
import GeneralSettings from "./UI/CustomGenerator/GeneralSettings";
|
||||
import {TabbedComponent} from "./UI/Base/TabbedComponent";
|
||||
import AllLayersPanel from "./UI/CustomGenerator/AllLayersPanel";
|
||||
import SharePanel from "./UI/CustomGenerator/SharePanel";
|
||||
import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty";
|
||||
import PageSplit from "./UI/Base/PageSplit";
|
||||
import HelpText from "./Customizations/HelpText";
|
||||
import {TagRendering} from "./Customizations/TagRendering";
|
||||
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
|
||||
|
||||
import {OsmConnection} from "./Logic/Osm/OsmConnection";
|
||||
import CustomGeneratorPanel from "./UI/CustomGenerator/CustomGeneratorPanel";
|
||||
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
|
||||
|
||||
let layout = GenerateEmpty.createEmptyLayout();
|
||||
if(window.location.hash.length > 10){
|
||||
if (window.location.hash.length > 10) {
|
||||
layout = JSON.parse(atob(window.location.hash.substr(1))) as LayoutConfigJson;
|
||||
} else {
|
||||
const hash = LocalStorageSource.Get("last-custom-theme").data
|
||||
if (hash !== undefined) {
|
||||
layout = JSON.parse(atob(hash)) as LayoutConfigJson;
|
||||
}
|
||||
}
|
||||
const es = new UIEventSource(layout);
|
||||
const encoded = es.map(config => btoa(JSON.stringify(config)));
|
||||
const liveUrl = encoded.map(encoded => `./index.html?userlayout=${es.data.id}#${encoded}`)
|
||||
const iframe = liveUrl.map(url => `<iframe src='${url}' width='100%' height='99%' style="box-sizing: border-box" title='Theme Preview'></iframe>`);
|
||||
|
||||
TagRendering.injectFunction();
|
||||
|
||||
const currentSetting = new UIEventSource<SingleSetting<any>>(undefined)
|
||||
const connection = new OsmConnection(false, new UIEventSource<string>(undefined), "customGenerator", false);
|
||||
|
||||
const generalSettings = new GeneralSettings(es, currentSetting);
|
||||
const languages = generalSettings.languages;
|
||||
|
||||
|
||||
// The preview
|
||||
const preview = new Combine([
|
||||
new VariableUiElement(iframe.stabilized(2500))
|
||||
]).SetClass("preview")
|
||||
|
||||
|
||||
new TabbedComponent([
|
||||
{
|
||||
header: "<img src='./assets/gear.svg'>",
|
||||
content:
|
||||
new PageSplit(
|
||||
generalSettings.SetStyle("width: 50vw;"),
|
||||
new Combine([
|
||||
new HelpText(currentSetting).SetStyle("height:calc(100% - 65vh); width: 100%; display:block; overflow-y: auto"),
|
||||
preview.SetStyle("height:65vh; width:100%; display:block")
|
||||
]).SetStyle("position:relative; width: 50%;")
|
||||
)
|
||||
},
|
||||
{
|
||||
header: "<img src='./assets/layers.svg'>",
|
||||
content: new AllLayersPanel(es, languages)
|
||||
},
|
||||
{
|
||||
header: "<img src='./assets/floppy.svg'>",
|
||||
content: new VariableUiElement(es.map(config => {
|
||||
return JSON.stringify(config, null, 2)
|
||||
.replace(/\n/g, "<br/>")
|
||||
.replace(/ /g, " ");
|
||||
}))
|
||||
|
||||
},
|
||||
{
|
||||
header: "<img src='./assets/share.svg'>",
|
||||
content: new SharePanel(es, liveUrl)
|
||||
}
|
||||
]).SetClass("main-tabs")
|
||||
new CustomGeneratorPanel(connection, layout)
|
||||
.AttachTo("maindiv");
|
||||
|
||||
|
|
439
index.css
439
index.css
|
@ -284,6 +284,9 @@
|
|||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
#hidden-on-mobile {
|
||||
display: none; /*Only shown on small screens*/
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-height: 600px) and (not (max-width:700px)) {
|
||||
|
@ -351,9 +354,6 @@
|
|||
padding-right: 10px; /* Shadow offset */
|
||||
}
|
||||
|
||||
#messagesboxmobilewrapper {
|
||||
display: none; /*Only shown on small screens*/
|
||||
}
|
||||
|
||||
.collapse-button {
|
||||
position: absolute;
|
||||
|
@ -556,6 +556,10 @@
|
|||
|
||||
|
||||
@media only screen and (max-width: 600px), only screen and (max-height: 600px) {
|
||||
#hidden-on-mobile {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#messagesbox-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
@ -590,33 +594,20 @@
|
|||
|
||||
}
|
||||
|
||||
#messagesboxmobilewrapper {
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
z-index: 5050;
|
||||
transition: all 500ms linear;
|
||||
overflow: hidden;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#messagesboxmobile-scroll {
|
||||
.messagesboxmobile-scroll {
|
||||
display: block;
|
||||
width: 100vw;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
overflow-y: scroll;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
height: calc(100% - 5em); /*Height of to-the-map is 5em*/
|
||||
height: calc(100vh - 5em); /*Height of to-the-map is 5em*/
|
||||
}
|
||||
|
||||
#messagesboxmobile {
|
||||
padding: 1em;
|
||||
padding-bottom: 2em;
|
||||
border-radius: 1em;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
|
@ -634,77 +625,77 @@
|
|||
box-shadow: unset;
|
||||
overflow-y: unset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#to-the-map {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#to-the-map > span{
|
||||
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
height: 3em;
|
||||
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
|
||||
padding-top: 0.75em;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
color: white;
|
||||
background-color: #7ebc6f;
|
||||
cursor: pointer;
|
||||
font-size: xx-large;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-height: 400px) {
|
||||
/* Landscape: small 'to the map' */
|
||||
#to-the-map {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
#to-the-map span {
|
||||
width: auto;
|
||||
border-top-left-radius: 1.5em;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: auto;
|
||||
margin:0;
|
||||
padding: 1em;
|
||||
padding-bottom: 0.75em;
|
||||
height: 3em;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#messagesboxmobile {
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
#messagesboxmobile-scroll {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
.to-the-map {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#welcomeMessage{
|
||||
box-shadow: unset;
|
||||
max-height: 100vh;
|
||||
height: 2.5em;
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
padding-top: 0.75em;
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: #7ebc6f;
|
||||
cursor: pointer;
|
||||
font-size: xx-large;
|
||||
font-weight: bold;
|
||||
border-top-left-radius: 0.5em;
|
||||
border-top-right-radius: 0.5em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 400px) {
|
||||
/* Landscape: small 'to the map' */
|
||||
#hidden-on-mobile {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
.to-the-map {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.to-the-map span {
|
||||
width: auto;
|
||||
border-top-left-radius: 1.5em;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
padding-bottom: 0.75em;
|
||||
height: 3em;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#messagesboxmobile {
|
||||
position: absolute;
|
||||
display: block;
|
||||
z-index: 10000;
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
.messagesboxmobile-scroll {
|
||||
display: block;
|
||||
width: 100vw;
|
||||
height: calc(100vh - 5em);
|
||||
box-sizing: border-box;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#welcomeMessage {
|
||||
box-shadow: unset;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.logo {
|
||||
float:right;
|
||||
|
@ -841,170 +832,173 @@
|
|||
|
||||
}
|
||||
|
||||
.attribution-author {
|
||||
display: inline-block;
|
||||
}
|
||||
.attribution-author {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.license {
|
||||
font-size: small;
|
||||
font-weight: lighter;
|
||||
}
|
||||
.license {
|
||||
font-size: small;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.attribution a {
|
||||
color: white;
|
||||
}
|
||||
.attribution a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
/**************** Image upload flow ***************/
|
||||
/**************** Image upload flow ***************/
|
||||
|
||||
.imageflow {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 2em;
|
||||
text-align: center;
|
||||
.imageflow {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 2em;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.imageflow-file-input-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.5em;
|
||||
border-radius: 1em;
|
||||
border: 3px solid black;
|
||||
}
|
||||
.imageflow-file-input-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.5em;
|
||||
border-radius: 1em;
|
||||
border: 3px solid black;
|
||||
}
|
||||
|
||||
.imageflow-add-picture {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
float: left;
|
||||
margin-top: 4px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 13px;
|
||||
}
|
||||
.imageflow-add-picture {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
float: left;
|
||||
margin-top: 4px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
.imageflow-file-input-wrapper img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0.1em;
|
||||
margin-top: 5px;
|
||||
border-radius: 0;
|
||||
float: left;
|
||||
}
|
||||
.imageflow-file-input-wrapper img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0.1em;
|
||||
margin-top: 5px;
|
||||
border-radius: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
.license-picker {
|
||||
float: left;
|
||||
}
|
||||
.license-picker {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.imageflow > input {
|
||||
display: none;
|
||||
}
|
||||
.imageflow > input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/***************** Info box (box containing features and questions ******************/
|
||||
/***************** Info box (box containing features and questions ******************/
|
||||
|
||||
.leaflet-popup-content {
|
||||
width: 40em !important;
|
||||
}
|
||||
|
||||
#messagesboxmobile .featureinfobox {
|
||||
max-height: unset;
|
||||
overflow-y: unset;
|
||||
}
|
||||
|
||||
#messagesboxmobile .featureinfobox > div {
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
padding-left: unset;
|
||||
}
|
||||
|
||||
.featureinfobox {
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.featureinfobox > div {
|
||||
width: calc(100% - 2em);
|
||||
padding-left: 1em;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
width: 40em !important;
|
||||
}
|
||||
|
||||
|
||||
.featureinfoboxtitle {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.question .form-text-field > input{
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#messagesboxmobile .featureinfobox {
|
||||
max-height: unset;
|
||||
overflow-y: unset;
|
||||
}
|
||||
|
||||
.osmlink {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
#messagesboxmobile .featureinfobox > div {
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
padding-left: unset;
|
||||
}
|
||||
|
||||
.osm-logo path {
|
||||
fill: #7ebc6f;
|
||||
}
|
||||
.featureinfobox {
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.featureinfoboxtitle .answer {
|
||||
display: inline;
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
.featureinfoboxtitle .answer-text {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.featureinfoboxtitle .editbutton {
|
||||
float: none;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
padding: 0.3em;
|
||||
border-radius: 0.35em;
|
||||
border: solid black 1px;
|
||||
margin-left: 0.5em;
|
||||
top: 0.2em;
|
||||
vertical-align: middle;
|
||||
|
||||
}
|
||||
.featureinfobox > div {
|
||||
width: calc(100% - 2em);
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
|
||||
.editbutton {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
padding: 0.5em;
|
||||
border-radius: 0.65em;
|
||||
border: solid black 1px;
|
||||
.featureinfoboxtitle {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
font-size: medium;
|
||||
float: right;
|
||||
.question .form-text-field > input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.osmlink {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.osm-logo path {
|
||||
fill: #7ebc6f;
|
||||
}
|
||||
|
||||
.featureinfoboxtitle .answer {
|
||||
display: inline;
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
.featureinfoboxtitle .answer-text {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.featureinfoboxtitle .editbutton {
|
||||
float: none;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
padding: 0.3em;
|
||||
border-radius: 0.35em;
|
||||
border: solid black 1px;
|
||||
margin-left: 0.5em;
|
||||
top: 0.2em;
|
||||
vertical-align: middle;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.editbutton {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
padding: 0.5em;
|
||||
border-radius: 0.65em;
|
||||
border: solid black 1px;
|
||||
|
||||
.wikipedialink {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.wikipedialink img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
font-size: medium;
|
||||
float: right;
|
||||
|
||||
|
||||
.featureinfoboxtitle span {
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
}
|
||||
}
|
||||
|
||||
.featureinfoboxtitle a {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
.wikipedialink {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.wikipedialink img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
|
||||
.featureinfoboxtitle span {
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
.featureinfoboxtitle a {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1273,6 +1267,7 @@
|
|||
padding: 0.5em;
|
||||
word-break: break-all;
|
||||
color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.iframe-code-block {
|
||||
|
|
27
index.html
27
index.html
|
@ -14,7 +14,7 @@
|
|||
<!-- $$$OG-META -->
|
||||
|
||||
<style>
|
||||
.decoration-desktop img {
|
||||
#decoration-desktop img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -23,33 +23,16 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<div style="position: absolute; left: 1em; top: 1em; width:35vh; height:35vh;" class="decoration-desktop">
|
||||
<div style="position: absolute; left: 1em; bottom: 1em; width:35vh; height:35vh;"
|
||||
id="decoration-desktop">
|
||||
<!-- A nice decoration while loading or on errors -->
|
||||
<!-- DECORATION 0 START -->
|
||||
<img src="./assets/add.svg"/>
|
||||
<!-- DECORATION 0 END -->
|
||||
</div>
|
||||
|
||||
<div id="messagesboxmobilewrapper">
|
||||
<div id="decoration" style="position: absolute; left: 1em; top: 1em; z-index: 1; width: 15em; height: 15em">
|
||||
<!-- A nice decoration while loading or on errors -->
|
||||
<!-- DECORATION 1 START -->
|
||||
<img src="./assets/add.svg"/>
|
||||
<!-- DECORATION 1 END -->
|
||||
</div>
|
||||
<div id="messagesboxmobile-scroll">
|
||||
<div id="messagesboxmobile">
|
||||
<div style="position: absolute; top: 50vh; left: 25vw; width:50vw; box-sizing: border-box;font-size: x-large">
|
||||
Loading MapComplete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="to-the-map">
|
||||
If this message persists:
|
||||
<ul style="margin:0">
|
||||
<li>Is javascript enabled?</li>
|
||||
<li>Is a scriptblocker (such as UMatrix or Brave) active?</li>
|
||||
</ul>
|
||||
<div id="hidden-on-mobile">
|
||||
<div id="messagesboxmobile">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
23
index.ts
23
index.ts
|
@ -2,7 +2,6 @@ import {TagRendering} from "./Customizations/TagRendering";
|
|||
import {UserBadge} from "./UI/UserBadge";
|
||||
import {CenterMessageBox} from "./UI/CenterMessageBox";
|
||||
import {TagUtils} from "./Logic/Tags";
|
||||
import {FullScreenMessageBoxHandler} from "./UI/FullScreenMessageBoxHandler";
|
||||
import {FeatureInfoBox} from "./UI/FeatureInfoBox";
|
||||
import {SimpleAddUI} from "./UI/SimpleAddUI";
|
||||
import {SearchAndGo} from "./UI/SearchAndGo";
|
||||
|
@ -17,6 +16,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters";
|
|||
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
|
||||
import {PersonalLayout} from "./Logic/PersonalLayout";
|
||||
import {FromJSON} from "./Customizations/JSON/FromJSON";
|
||||
import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler";
|
||||
|
||||
TagRendering.injectFunction();
|
||||
|
||||
|
@ -28,9 +28,9 @@ if (location.href.startsWith("http://buurtnatuur.be")) {
|
|||
window.location.replace("https://buurtnatuur.be");
|
||||
}
|
||||
|
||||
const testing = QueryParameters.GetQueryParameter("test", "true");
|
||||
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
|
||||
// Set to true if testing and changes should NOT be saved
|
||||
const testing = QueryParameters.GetQueryParameter("test", "true");
|
||||
testing.setData(testing.data ?? "true")
|
||||
// If you have a testfile somewhere, enable this to spoof overpass
|
||||
// This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
|
||||
|
@ -113,13 +113,17 @@ if (layoutToUse.hideFromOverview) {
|
|||
|
||||
if (layoutFromBase64 !== "false") {
|
||||
State.state.layoutDefinition = hash.substr(1);
|
||||
State.state.osmConnection.OnLoggedIn(() => {
|
||||
State.state.osmConnection.GetLongPreference("installed-theme-" + layoutToUse.id).setData(State.state.layoutDefinition);
|
||||
})
|
||||
if (!testing) {
|
||||
State.state.osmConnection.OnLoggedIn(() => {
|
||||
State.state.osmConnection.GetLongPreference("installed-theme-" + layoutToUse.id).setData(State.state.layoutDefinition);
|
||||
})
|
||||
}else{
|
||||
console.warn("NOT saving custom layout to OSM as we are tesing -> probably in an iFrame")
|
||||
}
|
||||
}
|
||||
InitUiElements.InitBaseMap();
|
||||
|
||||
new FixedUiElement("").AttachTo("decoration"); // Remove the decoration
|
||||
new FixedUiElement("").AttachTo("decoration-desktop"); // Remove the decoration
|
||||
|
||||
function setupAllLayerElements() {
|
||||
|
||||
|
@ -232,9 +236,12 @@ InitUiElements.OnlyIf((State.state.featureSwitchSearch), () => {
|
|||
new SearchAndGo().AttachTo("searchbox");
|
||||
});
|
||||
|
||||
new FullScreenMessageBoxHandler(() => {
|
||||
|
||||
new FullScreenMessageBox(() => {
|
||||
State.state.selectedElement.setData(undefined)
|
||||
}).update();
|
||||
}).AttachTo("messagesboxmobile");
|
||||
|
||||
|
||||
|
||||
InitUiElements.OnlyIf(State.state.featureSwitchWelcomeMessage, () => {
|
||||
InitUiElements.InitWelcomeMessage()
|
||||
|
|
Loading…
Reference in a new issue