forked from MapComplete/MapComplete
Further work on infobox, styling everything, removing clutter
This commit is contained in:
parent
2acd53d150
commit
0b4016b65d
48 changed files with 1283 additions and 454 deletions
|
@ -62,7 +62,7 @@ export class CenterMessageBox extends UIElement {
|
|||
if (this._centermessage.data != "") {
|
||||
pstyle.opacity = "1";
|
||||
pstyle.pointerEvents = "all";
|
||||
Helpers.registerActivateOsmAUthenticationClass(this._osmConnection);
|
||||
this._osmConnection.registerActivateOsmAUthenticationClass();
|
||||
return;
|
||||
}
|
||||
pstyle.pointerEvents = "none";
|
||||
|
|
115
UI/FeatureInfoBox.ts
Normal file
115
UI/FeatureInfoBox.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {TagMapping, TagMappingOptions} from "./TagMapping";
|
||||
import {Question, QuestionDefinition} from "../Logic/Question";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {VerticalCombine} from "./VerticalCombine";
|
||||
import {QuestionPicker} from "./QuestionPicker";
|
||||
import {OsmImageUploadHandler} from "../Logic/OsmImageUploadHandler";
|
||||
import {ImageCarousel} from "./Image/ImageCarousel";
|
||||
import {Changes} from "../Logic/Changes";
|
||||
import {UserDetails} from "../Logic/OsmConnection";
|
||||
import {Img} from "./Img";
|
||||
import {CommonTagMappings} from "../Layers/CommonTagMappings";
|
||||
import {Tag} from "../Logic/TagsFilter";
|
||||
|
||||
export class FeatureInfoBox extends UIElement {
|
||||
|
||||
private _tagsES: UIEventSource<any>;
|
||||
|
||||
|
||||
private _title: UIElement;
|
||||
private _osmLink: UIElement;
|
||||
private _infoElements: UIElement[]
|
||||
|
||||
|
||||
private _questions: QuestionPicker;
|
||||
|
||||
private _changes: Changes;
|
||||
private _userDetails: UIEventSource<UserDetails>;
|
||||
private _imageElement: ImageCarousel;
|
||||
|
||||
|
||||
constructor(
|
||||
tagsES: UIEventSource<any>,
|
||||
elementsToShow: (TagMappingOptions | QuestionDefinition | UIElement)[],
|
||||
questions: QuestionDefinition[],
|
||||
changes: Changes,
|
||||
userDetails: UIEventSource<UserDetails>,
|
||||
) {
|
||||
super(tagsES);
|
||||
this._tagsES = tagsES;
|
||||
this._changes = changes;
|
||||
this._userDetails = userDetails;
|
||||
|
||||
this._imageElement = new ImageCarousel(this._tagsES);
|
||||
|
||||
this._questions = new QuestionPicker(
|
||||
this._changes.asQuestions(questions), this._tagsES);
|
||||
|
||||
var infoboxes: UIElement[] = [];
|
||||
for (const uiElement of elementsToShow) {
|
||||
if (uiElement instanceof QuestionDefinition) {
|
||||
const questionDef = uiElement as QuestionDefinition;
|
||||
const question = new Question(this._changes, questionDef);
|
||||
infoboxes.push(question.CreateHtml(this._tagsES));
|
||||
} else if (uiElement instanceof TagMappingOptions) {
|
||||
const tagMappingOpt = uiElement as TagMappingOptions;
|
||||
infoboxes.push(new TagMapping(tagMappingOpt, this._tagsES))
|
||||
} else {
|
||||
const ui = uiElement as UIElement;
|
||||
infoboxes.push(ui);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this._title = infoboxes.shift();
|
||||
this._infoElements = infoboxes;
|
||||
|
||||
this._osmLink = new TagMapping(CommonTagMappings.osmLink, this._tagsES);
|
||||
|
||||
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
|
||||
|
||||
return "<div class='featureinfobox'>" +
|
||||
"<div class='featureinfoboxtitle'>" +
|
||||
"<span>" + this._title.Render() + "</span>" +
|
||||
this._osmLink.Render() +
|
||||
"</div>" +
|
||||
|
||||
"<div class='infoboxcontents'>" +
|
||||
|
||||
this._imageElement.Render() +
|
||||
|
||||
new VerticalCombine(this._infoElements).Render() +
|
||||
" <span class='infobox-questions'>" +
|
||||
this._questions.Render() +
|
||||
" </span>" +
|
||||
"</div>" +
|
||||
"" +
|
||||
"</div>";
|
||||
}
|
||||
|
||||
Activate() {
|
||||
super.Activate();
|
||||
this._imageElement.Activate();
|
||||
}
|
||||
|
||||
Update() {
|
||||
super.Update();
|
||||
this._imageElement.Update();
|
||||
}
|
||||
|
||||
private generateInfoBox() {
|
||||
var infoboxes: UIElement[] = [];
|
||||
|
||||
infoboxes.push(new OsmImageUploadHandler(
|
||||
this._tagsES, this._userDetails, this._changes
|
||||
).getUI());
|
||||
|
||||
|
||||
return new VerticalCombine(infoboxes);
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ export class ImageCarousel extends UIElement {
|
|||
this.slideshow = new SlideShow(
|
||||
new FixedUiElement("<b>Afbeeldingen</b>"),
|
||||
uiElements,
|
||||
new FixedUiElement("<i>Geen afbeeldingen gevonden</i>"));
|
||||
new FixedUiElement("")).HideOnEmpty(true);
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
|
|
|
@ -1,36 +1,52 @@
|
|||
import {UIEventSource} from "../UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {SimpleImageElement} from "./SimpleImageElement";
|
||||
import {LicenseInfo, Wikimedia} from "../../Logic/Wikimedia";
|
||||
|
||||
|
||||
export class WikimediaImage extends UIElement {
|
||||
|
||||
|
||||
static allLicenseInfos: any = {};
|
||||
private _imageMeta: UIEventSource<LicenseInfo>;
|
||||
private _imageLocation : string;
|
||||
|
||||
constructor(source: string) {
|
||||
super(undefined)
|
||||
this._imageLocation = source;
|
||||
if (WikimediaImage.allLicenseInfos[source] !== undefined) {
|
||||
this._imageMeta = WikimediaImage.allLicenseInfos[source];
|
||||
} else {
|
||||
this._imageMeta = new UIEventSource<LicenseInfo>(new LicenseInfo());
|
||||
WikimediaImage.allLicenseInfos[source] = this._imageMeta;
|
||||
}
|
||||
|
||||
constructor(source: UIEventSource<string>) {
|
||||
super(source)
|
||||
const meta = new UIEventSource<LicenseInfo>(new LicenseInfo());
|
||||
this.ListenTo(meta);
|
||||
this._imageMeta = meta;
|
||||
this._source.addCallback(() => {
|
||||
Wikimedia.LicenseData(source.data, (info) => {
|
||||
meta.setData(info);
|
||||
})
|
||||
});
|
||||
this._source.ping();
|
||||
this.ListenTo(this._imageMeta);
|
||||
|
||||
const self = this;
|
||||
Wikimedia.LicenseData(source, (info) => {
|
||||
self._imageMeta.setData(info);
|
||||
})
|
||||
}
|
||||
|
||||
protected InnerRender(): string {
|
||||
let url = Wikimedia.ImageNameToUrl(this._source.data);
|
||||
let url = Wikimedia.ImageNameToUrl(this._imageLocation, 500, 400);
|
||||
url = url.replace(/'/g, '%27');
|
||||
return "<div class='imgWithAttr'><img class='attributedImage' src='" + url + "' " +
|
||||
"alt='" + this._imageMeta.data.description + "' >" +
|
||||
"<br /><span class='attribution'>" +
|
||||
"<a href='https://commons.wikimedia.org/wiki/"+this._source.data+"' target='_blank'><b>" + (this._source.data) + "</b></a> <br />" +
|
||||
(this._imageMeta.data.artist ?? "Unknown artist") + " " + (this._imageMeta.data.licenseShortName ?? "") +
|
||||
"</span>" +
|
||||
|
||||
const wikimediaLink =
|
||||
"<a href='https://commons.wikimedia.org/wiki/" + this._imageLocation + "' target='_blank'>" +
|
||||
"<img class='wikimedia-link' src='./assets/wikimedia-commons-white.svg' alt='Wikimedia Commons Logo'/>" +
|
||||
"</a> ";
|
||||
|
||||
const attribution =
|
||||
"<span class='attribution-author'>" + (this._imageMeta.data.artist ?? "") + "</span>" + " <span class='license'>" + (this._imageMeta.data.licenseShortName ?? "") + "</span>";
|
||||
const image = "<img src='" + url + "' " + "alt='" + this._imageMeta.data.description + "' >";
|
||||
|
||||
return "<div class='imgWithAttr'>" +
|
||||
image +
|
||||
"<div class='attribution'>" +
|
||||
wikimediaLink +
|
||||
attribution +
|
||||
"</div>" +
|
||||
"</div>";
|
||||
}
|
||||
|
||||
|
|
|
@ -4,22 +4,28 @@ import {UIRadioButton} from "./UIRadioButton";
|
|||
import {VariableUiElement} from "./VariableUIElement";
|
||||
import $ from "jquery"
|
||||
import {Imgur} from "../Logic/Imgur";
|
||||
import {UserDetails} from "../Logic/OsmConnection";
|
||||
|
||||
export class ImageUploadFlow extends UIElement {
|
||||
private _licensePicker: UIRadioButton;
|
||||
private _licenseExplanation: UIElement;
|
||||
private _isUploading: UIEventSource<number> = new UIEventSource<number>(0)
|
||||
private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) };
|
||||
private _userdetails: UIEventSource<UserDetails>;
|
||||
|
||||
constructor(uploadOptions: ((license: string) =>
|
||||
{
|
||||
title: string,
|
||||
description: string,
|
||||
handleURL: ((url: string) => void),
|
||||
allDone: (() => void)
|
||||
})
|
||||
constructor(
|
||||
userInfo: UIEventSource<UserDetails>,
|
||||
uploadOptions: ((license: string) =>
|
||||
{
|
||||
title: string,
|
||||
description: string,
|
||||
handleURL: ((url: string) => void),
|
||||
allDone: (() => void)
|
||||
})
|
||||
) {
|
||||
super(undefined);
|
||||
this._userdetails = userInfo;
|
||||
this.ListenTo(userInfo);
|
||||
this._uploadOptions = uploadOptions;
|
||||
this.ListenTo(this._isUploading);
|
||||
this._licensePicker = UIRadioButton.FromStrings(
|
||||
|
@ -49,6 +55,10 @@ export class ImageUploadFlow extends UIElement {
|
|||
|
||||
protected InnerRender(): string {
|
||||
|
||||
if (!this._userdetails.data.loggedIn) {
|
||||
return "<div class='activate-osm-authentication'>Gelieve je aan te melden om een foto toe te voegen</div>";
|
||||
}
|
||||
|
||||
if (this._isUploading.data > 0) {
|
||||
return "<b>Bezig met uploaden, nog " + this._isUploading.data + " foto's te gaan...</b>"
|
||||
}
|
||||
|
@ -63,6 +73,13 @@ export class ImageUploadFlow extends UIElement {
|
|||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
super.InnerUpdate(htmlElement);
|
||||
const user = this._userdetails.data;
|
||||
if(!user.loggedIn){
|
||||
htmlElement.onclick = function(){
|
||||
user.osmConnection.AttemptLogin();
|
||||
}
|
||||
}
|
||||
|
||||
this._licensePicker.Update();
|
||||
const selector = document.getElementById('fileselector-' + this.id);
|
||||
const self = this;
|
||||
|
|
14
UI/Img.ts
Normal file
14
UI/Img.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
export class Img {
|
||||
|
||||
static osmAbstractLogo: string =
|
||||
"<svg class='osm-logo' xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" width=\"24px\" version=\"1.1\" viewBox=\"0 0 66 64\">" +
|
||||
" <g transform=\"translate(-0.849, -61)\">\n" +
|
||||
" <path d=\"M0.849,61 L6.414,75.609 L0.849,90.217 L6.414,104.826 L0.849,119.435 L4.266,120.739 L22.831,102.183 L26.162,102.696 L30.205,98.652 C27.819,95.888 26.033,92.59 25.057,88.948 L26.953,87.391 C26.627,85.879 26.449,84.313 26.449,82.704 C26.449,74.67 30.734,67.611 37.136,63.696 L30.066,61 L15.457,66.565 L0.849,61 z\"></path>" +
|
||||
" <path d=\"M48.71,64.617 C48.406,64.617 48.105,64.629 47.805,64.643 C47.52,64.657 47.234,64.677 46.953,64.704 C46.726,64.726 46.499,64.753 46.275,64.783 C46.039,64.814 45.811,64.847 45.579,64.887 C45.506,64.9 45.434,64.917 45.362,64.93 C45.216,64.958 45.072,64.987 44.927,65.017 C44.812,65.042 44.694,65.06 44.579,65.087 C44.442,65.119 44.307,65.156 44.17,65.191 C43.943,65.25 43.716,65.315 43.492,65.383 C43.323,65.433 43.155,65.484 42.988,65.539 C42.819,65.595 42.65,65.652 42.483,65.713 C42.475,65.716 42.466,65.719 42.457,65.722 C35.819,68.158 31.022,74.369 30.649,81.774 C30.633,82.083 30.622,82.391 30.622,82.704 C30.622,83.014 30.631,83.321 30.649,83.626 C30.649,83.629 30.648,83.632 30.649,83.635 C30.662,83.862 30.681,84.088 30.701,84.313 C31.466,93.037 38.377,99.948 47.101,100.713 C47.326,100.733 47.552,100.754 47.779,100.765 C47.782,100.765 47.785,100.765 47.788,100.765 C48.093,100.783 48.399,100.791 48.709,100.791 C53.639,100.791 58.096,98.833 61.353,95.652 C61.532,95.477 61.712,95.304 61.883,95.122 C61.913,95.09 61.941,95.058 61.97,95.026 C61.98,95.015 61.987,95.002 61.996,94.991 C62.132,94.845 62.266,94.698 62.396,94.548 C62.449,94.487 62.501,94.426 62.553,94.365 C62.594,94.316 62.634,94.267 62.675,94.217 C62.821,94.04 62.961,93.861 63.101,93.678 C63.279,93.444 63.456,93.199 63.622,92.956 C63.956,92.471 64.267,91.97 64.553,91.452 C64.661,91.257 64.757,91.06 64.857,90.861 C64.89,90.796 64.93,90.735 64.962,90.67 C64.98,90.633 64.996,90.594 65.014,90.556 C65.125,90.324 65.234,90.09 65.336,89.852 C65.349,89.82 65.365,89.789 65.379,89.756 C65.48,89.517 65.575,89.271 65.666,89.026 C65.678,88.994 65.689,88.962 65.701,88.93 C65.792,88.679 65.881,88.43 65.962,88.174 C65.97,88.148 65.98,88.122 65.988,88.096 C66.069,87.832 66.144,87.564 66.214,87.296 C66.219,87.275 66.226,87.255 66.231,87.235 C66.301,86.962 66.365,86.686 66.423,86.409 C66.426,86.391 66.428,86.374 66.431,86.356 C66.445,86.291 66.453,86.223 66.466,86.156 C66.511,85.925 66.552,85.695 66.588,85.461 C66.632,85.169 66.671,84.878 66.701,84.583 C66.701,84.574 66.701,84.565 66.701,84.556 C66.731,84.258 66.755,83.955 66.77,83.652 C66.77,83.646 66.77,83.641 66.77,83.635 C66.786,83.326 66.797,83.017 66.797,82.704 C66.797,72.69 58.723,64.617 48.71,64.617 z\"></path>" +
|
||||
" <path d=\"M62.936,99.809 C59.074,103.028 54.115,104.965 48.71,104.965 C47.101,104.965 45.535,104.787 44.023,104.461 L42.466,106.357 C39.007,105.43 35.855,103.781 33.179,101.574 L28.996,105.765 L29.51,108.861 L13.953,124.426 L15.457,125 L30.066,119.435 L44.675,125 L59.283,119.435 L64.849,104.826 L62.936,99.809 z\"></path>" +
|
||||
" </g>" +
|
||||
"</svg>";
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UserDetails} from "../Logic/OsmConnection";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
|
||||
export class LoginDependendMessage extends UIElement {
|
||||
private _noLoginMsg: string;
|
||||
private _loginMsg: string;
|
||||
private _userDetails: UserDetails;
|
||||
|
||||
constructor(loginData: UIEventSource<UserDetails>,
|
||||
noLoginMsg: string,
|
||||
loginMsg: string) {
|
||||
super(loginData);
|
||||
this._userDetails = loginData.data;
|
||||
this._noLoginMsg = noLoginMsg;
|
||||
this._loginMsg = loginMsg;
|
||||
}
|
||||
|
||||
protected InnerRender(): string {
|
||||
if (this._userDetails.loggedIn) {
|
||||
return this._loginMsg;
|
||||
} else {
|
||||
return this._noLoginMsg;
|
||||
}
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
// pass
|
||||
}
|
||||
|
||||
}
|
58
UI/MessageBoxHandler.ts
Normal file
58
UI/MessageBoxHandler.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Keeps 'messagebox' and 'messageboxmobile' in sync, shows a 'close' button on the latter one
|
||||
*/
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {UIElement} from "./UIElement";
|
||||
import {FixedUiElement} from "./FixedUiElement";
|
||||
import {VariableUiElement} from "./VariableUIElement";
|
||||
|
||||
export class MessageBoxHandler {
|
||||
private _uielement: UIEventSource<() => UIElement>;
|
||||
|
||||
constructor(uielement: UIEventSource<() => UIElement>,
|
||||
onClear: (() => void)) {
|
||||
this._uielement = uielement;
|
||||
this.listenTo(uielement);
|
||||
this.update();
|
||||
|
||||
new VariableUiElement(new UIEventSource<string>("<h2>Naar de kaart > </h2>"),
|
||||
(htmlElement) => {
|
||||
htmlElement.onclick = function () {
|
||||
uielement.setData(undefined);
|
||||
onClear();
|
||||
}
|
||||
}
|
||||
).AttachTo("to-the-map");
|
||||
|
||||
}
|
||||
|
||||
listenTo(uiEventSource: UIEventSource<any>) {
|
||||
const self = this;
|
||||
uiEventSource.addCallback(function () {
|
||||
self.update();
|
||||
})
|
||||
}
|
||||
|
||||
update() {
|
||||
const wrapper = document.getElementById("messagesboxmobilewrapper");
|
||||
const gen = this._uielement.data;
|
||||
console.log("Generator: ", gen);
|
||||
if (gen === undefined) {
|
||||
wrapper.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
wrapper.classList.remove("hidden");
|
||||
gen()
|
||||
?.HideOnEmpty(true)
|
||||
?.AttachTo("messagesbox")
|
||||
?.Activate();
|
||||
|
||||
gen()
|
||||
?.HideOnEmpty(true)
|
||||
?.AttachTo("messagesboxmobile")
|
||||
?.Activate();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +1,37 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIEventSource} from "./UIEventSource";
|
||||
import {Changes} from "../Logic/Changes";
|
||||
|
||||
export class PendingChanges extends UIElement{
|
||||
export class PendingChanges extends UIElement {
|
||||
private _pendingChangesCount: UIEventSource<number>;
|
||||
private _countdown: UIEventSource<number>;
|
||||
private _isSaving: UIEventSource<boolean>;
|
||||
|
||||
private readonly changes;
|
||||
|
||||
constructor(changes: Changes, countdown: UIEventSource<number>) {
|
||||
super(undefined); // We do everything manually here!
|
||||
this.changes = changes;
|
||||
|
||||
|
||||
countdown.addCallback(function () {
|
||||
|
||||
const percentage = Math.max(0, 100 * countdown.data / 20000);
|
||||
|
||||
let bar = document.getElementById("pending-bar");
|
||||
if (bar === undefined) {
|
||||
return;
|
||||
}
|
||||
const style = bar.style;
|
||||
style.width = percentage + "%";
|
||||
style["margin-left"] = (50 - (percentage / 2)) + "%";
|
||||
|
||||
});
|
||||
|
||||
changes.pendingChangesES.addCallback(function () {
|
||||
const c = changes._pendingChanges.length;
|
||||
const text = document.getElementById("pending-text");
|
||||
if (c == 0) {
|
||||
text.style.opacity = "0";
|
||||
text.innerText = "Saving...";
|
||||
} else {
|
||||
text.innerText = c + " pending";
|
||||
text.style.opacity = "1";
|
||||
}
|
||||
|
||||
|
||||
const bar = document.getElementById("pending-bar");
|
||||
|
||||
if (bar === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (c == 0) {
|
||||
bar.style.opacity = "0";
|
||||
} else {
|
||||
bar.style.opacity = "0.5";
|
||||
}
|
||||
|
||||
});
|
||||
constructor(pendingChangesCount: UIEventSource<number>,
|
||||
countdown: UIEventSource<number>,
|
||||
isSaving: UIEventSource<boolean>) {
|
||||
super(pendingChangesCount);
|
||||
this.ListenTo(isSaving);
|
||||
this.ListenTo(countdown);
|
||||
this._pendingChangesCount = pendingChangesCount;
|
||||
this._countdown = countdown;
|
||||
this._isSaving = isSaving;
|
||||
}
|
||||
|
||||
|
||||
protected InnerRender(): string {
|
||||
return "<div id='pending-bar' style='width:100%; margin-left:0%'></div>" +
|
||||
"<div id='pending-text'></div>";
|
||||
}
|
||||
if (this._isSaving.data) {
|
||||
return "<span class='alert'>Saving</span>";
|
||||
}
|
||||
if (this._pendingChangesCount.data == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var restingSeconds = this._countdown.data / 1000;
|
||||
var dots = "";
|
||||
while (restingSeconds > 0) {
|
||||
dots += ".";
|
||||
restingSeconds = restingSeconds - 1;
|
||||
}
|
||||
return "Saving "+this._pendingChangesCount.data;
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -36,7 +36,7 @@ export class QuestionPicker extends UIElement {
|
|||
|
||||
|
||||
if (highestQ === undefined) {
|
||||
return "De vragen zijn op!";
|
||||
return "";
|
||||
}
|
||||
|
||||
return highestQ.CreateHtml(this.source).Render();
|
||||
|
|
|
@ -6,7 +6,6 @@ export class SlideShow extends UIElement {
|
|||
private readonly _embeddedElements: UIEventSource<UIElement[]>
|
||||
|
||||
private readonly _currentSlide: UIEventSource<number> = new UIEventSource<number>(0);
|
||||
private readonly _title: UIElement;
|
||||
private readonly _noimages: UIElement;
|
||||
|
||||
constructor(
|
||||
|
@ -14,7 +13,6 @@ export class SlideShow extends UIElement {
|
|||
embeddedElements: UIEventSource<UIElement[]>,
|
||||
noImages: UIElement) {
|
||||
super(embeddedElements);
|
||||
this._title = title;
|
||||
this._embeddedElements = embeddedElements;
|
||||
this.ListenTo(this._currentSlide);
|
||||
this._noimages = noImages;
|
||||
|
@ -24,30 +22,37 @@ export class SlideShow extends UIElement {
|
|||
if (this._embeddedElements.data.length == 0) {
|
||||
return this._noimages.Render();
|
||||
}
|
||||
const prevBtn = "<input class='prev-button' type='button' onclick='console.log(\"prev\")' value='<' />"
|
||||
const nextBtn = "<input class='next-button' type='button' onclick='console.log(\"nxt\")' value='>' />"
|
||||
let header = this._title.Render();
|
||||
if (this._embeddedElements.data.length > 1) {
|
||||
header = header + prevBtn + (this._currentSlide.data + 1) + "/" + this._embeddedElements.data.length + nextBtn;
|
||||
|
||||
if (this._embeddedElements.data.length == 1) {
|
||||
return "<div class='image-slideshow'>"+this._embeddedElements.data[0].Render()+"</div>";
|
||||
}
|
||||
let body = ""
|
||||
|
||||
const prevBtn = "<div class='prev-button' id='prevbtn-"+this.id+"'></div>"
|
||||
const nextBtn = "<div class='next-button' id='nextbtn-"+this.id+"'></div>"
|
||||
|
||||
let slides = ""
|
||||
for (let i = 0; i < this._embeddedElements.data.length; i++) {
|
||||
let embeddedElement = this._embeddedElements.data[i];
|
||||
let state = "hidden"
|
||||
if (this._currentSlide.data === i) {
|
||||
state = "active-slide";
|
||||
}
|
||||
body += " <div class=\"slide " + state + "\">" + embeddedElement.Render() + "</div>\n";
|
||||
slides += " <div class=\"slide " + state + "\">" + embeddedElement.Render() + "</div>\n";
|
||||
}
|
||||
return "<span class='image-slideshow'>" + header + body + "</span>";
|
||||
return "<div class='image-slideshow'>"
|
||||
+ prevBtn
|
||||
+ "<div class='slides'>" + slides + "</div>"
|
||||
+ nextBtn
|
||||
+ "</div>";
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement) {
|
||||
const nextButton = htmlElement.getElementsByClassName('next-button')[0];
|
||||
if(nextButton === undefined){
|
||||
const nextButton = document.getElementById("nextbtn-"+this.id);
|
||||
if(nextButton === undefined || nextButton === null){
|
||||
return;
|
||||
}
|
||||
const prevButton = htmlElement.getElementsByClassName('prev-button')[0];
|
||||
|
||||
const prevButton = document.getElementById("prevbtn-"+this.id);
|
||||
const self = this;
|
||||
nextButton.onclick = () => {
|
||||
const current = self._currentSlide.data;
|
||||
|
|
|
@ -6,6 +6,8 @@ export abstract class UIElement {
|
|||
|
||||
public readonly id: string;
|
||||
public readonly _source: UIEventSource<any>;
|
||||
|
||||
private _hideIfEmpty = false;
|
||||
|
||||
protected constructor(source: UIEventSource<any>) {
|
||||
this.id = "ui-element-" + UIElement.nextId;
|
||||
|
@ -33,9 +35,23 @@ export abstract class UIElement {
|
|||
}
|
||||
|
||||
element.innerHTML = this.InnerRender();
|
||||
if(this._hideIfEmpty){
|
||||
if(element.innerHTML === ""){
|
||||
element.parentElement.style.display = "none";
|
||||
}else{
|
||||
element.parentElement.style.display = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
this.InnerUpdate(element);
|
||||
}
|
||||
|
||||
HideOnEmpty(hide : boolean){
|
||||
this._hideIfEmpty = hide;
|
||||
this.Update();
|
||||
return this;
|
||||
}
|
||||
|
||||
// Called after the HTML has been replaced. Can be used for css tricks
|
||||
InnerUpdate(htmlElement : HTMLElement){}
|
||||
|
||||
|
@ -45,6 +61,10 @@ export abstract class UIElement {
|
|||
|
||||
AttachTo(divId: string) {
|
||||
let element = document.getElementById(divId);
|
||||
if(element === null){
|
||||
console.log("SEVERE: could not attach UIElement to ", divId);
|
||||
return;
|
||||
}
|
||||
element.innerHTML = this.Render();
|
||||
this.Update();
|
||||
return this;
|
||||
|
|
|
@ -8,7 +8,7 @@ export class UIEventSource<T>{
|
|||
}
|
||||
|
||||
|
||||
public addCallback(callback: (() => void)) {
|
||||
public addCallback(callback: ((latestData) => void)) {
|
||||
this._callbacks.push(callback);
|
||||
return this;
|
||||
}
|
||||
|
@ -23,18 +23,19 @@ export class UIEventSource<T>{
|
|||
|
||||
public ping(): void {
|
||||
for (let i in this._callbacks) {
|
||||
this._callbacks[i]();
|
||||
this._callbacks[i](this.data);
|
||||
}
|
||||
}
|
||||
|
||||
public map<J>(f: ((T) => J)): UIEventSource<J> {
|
||||
const newSource = new UIEventSource<J>(
|
||||
f(this.data)
|
||||
);
|
||||
const self = this;
|
||||
this.addCallback(function () {
|
||||
newSource.setData(f(self.data));
|
||||
newSource.ping();
|
||||
});
|
||||
const newSource = new UIEventSource<J>(
|
||||
f(this.data)
|
||||
);
|
||||
|
||||
return newSource;
|
||||
|
||||
|
|
|
@ -7,11 +7,14 @@ import {UIEventSource} from "./UIEventSource";
|
|||
*/
|
||||
export class UserBadge extends UIElement {
|
||||
private _userDetails: UIEventSource<UserDetails>;
|
||||
private _pendingChanges: UIElement;
|
||||
|
||||
|
||||
constructor(userDetails: UIEventSource<UserDetails>) {
|
||||
constructor(userDetails: UIEventSource<UserDetails>,
|
||||
pendingChanges : UIElement) {
|
||||
super(userDetails);
|
||||
this._userDetails = userDetails;
|
||||
this._pendingChanges = pendingChanges;
|
||||
|
||||
userDetails.addCallback(function () {
|
||||
const profilePic = document.getElementById("profile-pic");
|
||||
|
@ -27,17 +30,50 @@ export class UserBadge extends UIElement {
|
|||
if (!user.loggedIn) {
|
||||
return "<div class='activate-osm-authentication'>Klik hier om aan te melden bij OSM</div>";
|
||||
}
|
||||
|
||||
|
||||
let messageSpan = "<span id='messages'>" +
|
||||
" <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='envelope' src='./assets/envelope.svg'/>" +
|
||||
user.totalMessages +
|
||||
"</a></span>";
|
||||
|
||||
if (user.unreadMessages > 0) {
|
||||
messageSpan = "<span id='messages' class='alert'>" +
|
||||
" <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='envelope' src='./assets/envelope.svg'/>" +
|
||||
" " +
|
||||
"" +
|
||||
user.unreadMessages.toString() +
|
||||
"</a></span>";
|
||||
}
|
||||
|
||||
let dryrun = "";
|
||||
if (user.dryRun) {
|
||||
dryrun = " <span class='alert'>TESTING</span>";
|
||||
}
|
||||
|
||||
return "<img id='profile-pic' src='" + user.img + "'/> " +
|
||||
"<div id='usertext'>"+
|
||||
"<div id='username'>" +
|
||||
"<a href='https://www.openstreetmap.org/user/"+user.name+"' target='_blank'>" + user.name + "</a></div> <br />" +
|
||||
"<div id='csCount'> " +
|
||||
" <a href='https://www.openstreetmap.org/user/"+user.name+"/history' target='_blank'><img class='star' src='./assets/star.svg'/>" + user.csCount + "</div></a>" +
|
||||
"<div id='usertext'>" +
|
||||
"<p id='username'>" +
|
||||
"<a href='https://www.openstreetmap.org/user/" + user.name + "' target='_blank'>" + user.name + "</a>" +
|
||||
dryrun +
|
||||
"</p> " +
|
||||
"<p id='userstats'>" +
|
||||
messageSpan +
|
||||
"<span id='csCount'> " +
|
||||
" <a href='https://www.openstreetmap.org/user/" + user.name + "/history' target='_blank'><img class='star' src='./assets/star.svg'/> " + user.csCount +
|
||||
"</a></span> " +
|
||||
this._pendingChanges.Render() +
|
||||
"</p>" +
|
||||
|
||||
"</div>";
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
this._pendingChanges.Update();
|
||||
}
|
||||
|
||||
Activate() {
|
||||
this._pendingChanges.Activate();
|
||||
}
|
||||
|
||||
}
|
|
@ -3,15 +3,25 @@ import {UIEventSource} from "./UIEventSource";
|
|||
|
||||
export class VariableUiElement extends UIElement {
|
||||
private _html: UIEventSource<string>;
|
||||
private _innerUpdate: (htmlElement: HTMLElement) => void;
|
||||
|
||||
constructor(html: UIEventSource<string>) {
|
||||
constructor(html: UIEventSource<string>, innerUpdate : ((htmlElement : HTMLElement) => void) = undefined) {
|
||||
super(html);
|
||||
this._html = html;
|
||||
this._innerUpdate = innerUpdate;
|
||||
|
||||
}
|
||||
|
||||
protected InnerRender(): string {
|
||||
return this._html.data;
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
super.InnerUpdate(htmlElement);
|
||||
if(this._innerUpdate !== undefined){
|
||||
this._innerUpdate(htmlElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue