From 9777a2666b11ac732100aaa80685a407ab2abeea Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 13 Sep 2020 03:29:44 +0200 Subject: [PATCH] More cleanup of code: remove the overly complicated layer selection --- Customizations/LayerDefinition.ts | 2 +- InitUiElements.ts | 47 ++++-- Logic/ImageSearcher.ts | 53 ++----- UI/Image/DeleteImage.ts | 53 +++++++ UI/Image/ImageCarousel.ts | 15 +- UI/Image/ImgurImage.ts | 2 +- UI/Image/WikimediaImage.ts | 2 +- UI/LayerSelection.ts | 47 +++--- UI/SlideShow.ts | 9 +- UI/i18n/Translations.ts | 4 + index.css | 253 ++++++------------------------ index.html | 3 +- test.ts | 13 +- 13 files changed, 199 insertions(+), 304 deletions(-) create mode 100644 UI/Image/DeleteImage.ts diff --git a/Customizations/LayerDefinition.ts b/Customizations/LayerDefinition.ts index 10b5d3e..369c59d 100644 --- a/Customizations/LayerDefinition.ts +++ b/Customizations/LayerDefinition.ts @@ -17,7 +17,7 @@ export class LayerDefinition { /** * This name is used in the 'hide or show this layer'-buttons */ - name: string | UIElement; + name: string | Translation; /*** * This is shown under the 'add new' button to indicate what kind of feature one is adding. diff --git a/InitUiElements.ts b/InitUiElements.ts index ce77ec2..f69f7d0 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -345,32 +345,57 @@ export class InitUiElements { ), Locale.language); } - static InitLayerSelection() { - const closedFilterButton = ``; - - const openFilterButton = ``; - + private static GenerateLayerControlPanel() { let baseLayerOptions = BaseLayers.baseLayers.map((layer) => { return {value: layer, shown: layer.name} }); - const backgroundMapPicker = new Combine([new DropDown(`Background map`, baseLayerOptions, State.state.bm.CurrentLayer), openFilterButton]); - const layerSelection = new Combine([`

Maplayers

`, new LayerSelection()]); - let layerControl = backgroundMapPicker; + let layerControlPanel = new Combine([new DropDown(Translations.t.general.backgroundMap, baseLayerOptions, State.state.bm.CurrentLayer)]); + layerControlPanel.SetStyle("margin:1em"); if (State.state.filteredLayers.data.length > 1) { - layerControl = new Combine([layerSelection, backgroundMapPicker]); + const layerSelection = new LayerSelection(); + layerControlPanel = new Combine([layerSelection, layerControlPanel]); } + return layerControlPanel; + } + static InitLayerSelection() { InitUiElements.OnlyIf(State.state.featureSwitchLayers, () => { - const checkbox = new CheckBox(layerControl, closedFilterButton, + const layerControlPanel = this.GenerateLayerControlPanel() + .SetStyle("display:block;padding:1em;border-radius:1em;"); + const closeButton = new Combine([Img.openFilterButton]) + .SetStyle("display:block; width: min-content; background: #e5f5ff;padding:1em; border-radius:1em;"); + const checkbox = new CheckBox( + new Combine([ + closeButton, + layerControlPanel]).SetStyle("display:flex;flex-direction:row;") + , + new Combine([Img.closedFilterButton]) + .SetStyle("display:block;border-radius:50%;background:white;padding:1em;"), QueryParameters.GetQueryParameter("layer-control-toggle", "false") .map((str) => str !== "false", [], b => "" + b) ); - checkbox.AttachTo("filter__selection"); + checkbox + .AttachTo("layer-selection"); + + State.state.bm.Location.addCallback(() => { + // Close the layer selection when the map is moved checkbox.isEnabled.setData(false); }); + const fullScreen = this.GenerateLayerControlPanel(); + checkbox.isEnabled.addCallback(isEnabled => { + if (isEnabled) { + State.state.fullScreenMessage.setData(fullScreen); + } + }) + State.state.fullScreenMessage.addCallbackAndRun(latest => { + if (latest === undefined) { + checkbox.isEnabled.setData(false); + } + }) + }); } diff --git a/Logic/ImageSearcher.ts b/Logic/ImageSearcher.ts index 700e7a5..c75593a 100644 --- a/Logic/ImageSearcher.ts +++ b/Logic/ImageSearcher.ts @@ -2,10 +2,8 @@ import {WikimediaImage} from "../UI/Image/WikimediaImage"; import {SimpleImageElement} from "../UI/Image/SimpleImageElement"; import {UIElement} from "../UI/UIElement"; import {ImgurImage} from "../UI/Image/ImgurImage"; -import {State} from "../State"; import {ImagesInCategory, Wikidata, Wikimedia} from "./Web/Wikimedia"; import {UIEventSource} from "./UIEventSource"; -import {Tag} from "./Tags"; /** * There are multiple way to fetch images for an object @@ -22,12 +20,11 @@ import {Tag} from "./Tags"; * Class which search for all the possible locations for images and which builds a list of UI-elements for it. * Note that this list is embedded into an UIEVentSource, ready to put it into a carousel */ -export class ImageSearcher extends UIEventSource { +export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> { private readonly _tags: UIEventSource; private readonly _wdItem = new UIEventSource(""); private readonly _commons = new UIEventSource(""); - public _deletedImages = new UIEventSource([]); constructor(tags: UIEventSource) { @@ -45,12 +42,12 @@ export class ImageSearcher extends UIEventSource { wikidataId = wikidataId.substr(1); } Wikimedia.GetWikiData(parseInt(wikidataId), (wd: Wikidata) => { - self.AddImage(wd.image); + self.AddImage(undefined, wd.image); Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => { for (const image of images.images) { // @ts-ignore if (image.startsWith("File:")) { - self.AddImage(image); + self.AddImage(undefined, image); } } }) @@ -69,13 +66,13 @@ export class ImageSearcher extends UIEventSource { for (const image of images.images) { // @ts-ignore if (image.startsWith("File:")) { - self.AddImage(image); + self.AddImage(undefined, image); } } }) } else { // @ts-ignore if (commons.startsWith("File:")) { - self.AddImage(commons); + self.AddImage(undefined, commons); } } } @@ -84,62 +81,34 @@ export class ImageSearcher extends UIEventSource { } - private AddImage(url: string) { + private AddImage(key: string, url: string) { if (url === undefined || url === null || url === "") { return; } for (const el of this.data) { - if (el === url) { + if (el.url === url) { return; } } - this.data.push(url); + this.data.push({key:key, url:url}); this.ping(); } - - private ImageKey(url: string): string { - const tgs = this._tags.data; - for (const key in tgs) { - if (tgs[key] === url) { - return key; - } - } - return undefined; - } - - public IsDeletable(url: string): boolean { - return this.ImageKey(url) !== undefined; - } - - public Delete(url: string): void { - - const key = this.ImageKey(url); - if (key === undefined) { - return; - } - console.log("Deleting image...", key, " --> ", url); - this._deletedImages.data.push(url); - this._deletedImages.ping(); - this.ping(); - State.state?.changes?.addTag(this._tags.data.id, new Tag(key, "")); - } - - + private LoadImages(): void { const imageTag = this._tags.data.image; if (imageTag !== undefined) { const bareImages = imageTag.split(";"); for (const bareImage of bareImages) { - this.AddImage(bareImage); + this.AddImage("image", bareImage); } } for (const key in this._tags.data) { if (key.startsWith("image:")) { const url = this._tags.data[key] - this.AddImage(url); + this.AddImage(key, url); } } diff --git a/UI/Image/DeleteImage.ts b/UI/Image/DeleteImage.ts new file mode 100644 index 0000000..cf56f97 --- /dev/null +++ b/UI/Image/DeleteImage.ts @@ -0,0 +1,53 @@ +import {UIElement} from "../UIElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Translations from "../i18n/Translations"; +import {CheckBox} from "../Input/CheckBox"; +import Combine from "../Base/Combine"; +import {State} from "../../State"; +import {Tag} from "../../Logic/Tags"; + + +export default class DeleteImage extends UIElement { + private readonly key: string; + private readonly tags: UIEventSource; + + private readonly isDeletedBadge: UIElement; + private readonly deleteDialog: UIElement; + + constructor(key: string, tags: UIEventSource) { + super(tags); + this.tags = tags; + this.key = key; + + this.isDeletedBadge = Translations.t.image.isDeleted; + + const style = "display:block;color:white;width:100%;" + const deleteButton = Translations.t.image.doDelete.Clone() + .SetStyle(style+"background:#ff8c8c;") + .onClick(() => { + State.state?.changes.addTag(tags.data.id, new Tag(key, "")); + }); + + const cancelButton = Translations.t.general.cancel; + this.deleteDialog = new CheckBox( + new Combine([ + deleteButton, + cancelButton + + ]).SetStyle("display:flex;flex-direction:column;"), + "" + ) + + } + + InnerRender(): string { + + const value = this.tags.data[this.key]; + if (value === undefined || value === "") { + return this.isDeletedBadge.Render(); + } + + return this.deleteDialog.Render(); + } + +} \ No newline at end of file diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index 3714ab1..47b6601 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -8,6 +8,8 @@ import { TagDependantUIElementConstructor } from "../../Customizations/UIElementConstructor"; import Translation from "../i18n/Translation"; +import Combine from "../Base/Combine"; +import DeleteImage from "./DeleteImage"; export class ImageCarouselConstructor implements TagDependantUIElementConstructor { IsKnown(properties: any): boolean { @@ -34,16 +36,21 @@ export class ImageCarouselConstructor implements TagDependantUIElementConstructo export class ImageCarousel extends TagDependantUIElement { - public readonly searcher: ImageSearcher; public readonly slideshow: SlideShow; constructor(tags: UIEventSource) { super(tags); - this.searcher = new ImageSearcher(tags); - const uiElements = this.searcher.map((imageURLS: string[]) => { + const searcher : UIEventSource<{url:string}[]> = new ImageSearcher(tags); + const uiElements = searcher.map((imageURLS: {key: string, url:string}[]) => { const uiElements: UIElement[] = []; for (const url of imageURLS) { - const image = ImageSearcher.CreateImageElement(url); + let image = ImageSearcher.CreateImageElement(url.url); + if(url.key !== undefined){ + image = new Combine([ + image, + new DeleteImage(url.key, tags) + ]); + } uiElements.push(image); } return uiElements; diff --git a/UI/Image/ImgurImage.ts b/UI/Image/ImgurImage.ts index dbeefac..adabd2e 100644 --- a/UI/Image/ImgurImage.ts +++ b/UI/Image/ImgurImage.ts @@ -4,7 +4,7 @@ import {LicenseInfo} from "../../Logic/Web/Wikimedia"; import {Imgur} from "../../Logic/Web/Imgur"; -export class ImgurImage extends UIElement { +export class ImgurImage extends UIElement { /*** diff --git a/UI/Image/WikimediaImage.ts b/UI/Image/WikimediaImage.ts index 68a7688..e9ca5f3 100644 --- a/UI/Image/WikimediaImage.ts +++ b/UI/Image/WikimediaImage.ts @@ -35,7 +35,7 @@ export class WikimediaImage extends UIElement { const wikimediaLink = "" + - "Wikimedia Commons Logo" + + "Wikimedia Commons Logo" + " "; const attribution = diff --git a/UI/LayerSelection.ts b/UI/LayerSelection.ts index 14e9eb6..f9fad8c 100644 --- a/UI/LayerSelection.ts +++ b/UI/LayerSelection.ts @@ -1,10 +1,9 @@ -import { UIElement } from "./UIElement"; -import { FilteredLayer } from "../Logic/FilteredLayer"; -import { CheckBox } from "./Input/CheckBox"; +import {UIElement} from "./UIElement"; +import {CheckBox} from "./Input/CheckBox"; import Combine from "./Base/Combine"; -import {Utils} from "../Utils"; import {Img} from "./Img"; import {State} from "../State"; +import Translations from "./i18n/Translations"; export class LayerSelection extends UIElement { @@ -15,37 +14,27 @@ export class LayerSelection extends UIElement { this._checkboxes = []; for (const layer of State.state.filteredLayers.data) { - const checkbox = ` - - `; - let icon = ""; + const checkbox = Img.checkmark; + let icon = ""; if (layer.layerDef.icon && layer.layerDef.icon !== "") { - icon = `` + icon = `` } - const name = layer.layerDef.name; + const name = Translations.WT(layer.layerDef.name).Clone() + .SetStyle("font-size:large;"); this._checkboxes.push(new CheckBox( - new Combine([checkbox, icon, name]), - new Combine([ - Img.no_checkmark, - icon, - layer.layerDef.name]), - layer.isDisplayed)); - } + new Combine([checkbox, icon, name]), + new Combine([Img.no_checkmark, icon, name]), + layer.isDisplayed) + .SetStyle("margin:0.3em;") + ); + } } - InnerRender(): string { - let html = ``; - - for (const checkBox of this._checkboxes) { - const checkBoxHTML = checkBox.Render(); - const checkBoxListItem = `
  • ${checkBoxHTML}
  • `; - - html = html + checkBoxListItem; + InnerRender(): string { + return new Combine(this._checkboxes) + .SetStyle("display:flex;flex-direction:column;") + .Render(); } - - return `
      ${html}
    `; - } - } \ No newline at end of file diff --git a/UI/SlideShow.ts b/UI/SlideShow.ts index 6852717..cc18476 100644 --- a/UI/SlideShow.ts +++ b/UI/SlideShow.ts @@ -71,5 +71,12 @@ export class SlideShow extends UIElement { index = index % this._embeddedElements.data.length; this._currentSlide.setData(index); } + + Update() { + super.Update(); + for (const uiElement of this._embeddedElements.data) { + uiElement.Update(); + } + } - } \ No newline at end of file +} \ No newline at end of file diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index 62c6483..2530cf7 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -1015,6 +1015,10 @@ export default class Translations { "

    " + "

    Merk je een bug of wil je een extra feature? Wil je helpen vertalen? Bezoek dan de broncode en issue tracker

    ", + }), + backgroundMap: new T({ + "en":"Background map", + "nl":"Achtergrondkaart" }) diff --git a/index.css b/index.css index 56b78c6..86aa3bf 100644 --- a/index.css +++ b/index.css @@ -1,56 +1,66 @@ - html, body { - height: 100%; - margin: 0; - padding: 0; - } +html, body { + height: 100%; + margin: 0; + padding: 0; +} - body { - font-family: 'Helvetica Neue', Arial, sans-serif; - } +body { + font-family: 'Helvetica Neue', Arial, sans-serif; +} - #leafletDiv { - height: 100%; - } +#leafletDiv { + height: 100%; +} - #geolocate-button { - position: absolute; - bottom: 25px; - right: 50px; - z-index: 999; /*Just below leaflets zoom*/ - background-color: white; - border-radius: 5px; - border: solid 2px #0005; - cursor: pointer; - width: 43px; - height: 43px; - display: none; /*Hidden by default, only visible on mobile*/ - } +#geolocate-button { + position: absolute; + bottom: 25px; + right: 50px; + z-index: 999; /*Just below leaflets zoom*/ + background-color: white; + border-radius: 5px; + border: solid 2px #0005; + cursor: pointer; + width: 43px; + height: 43px; + display: none; /*Hidden by default, only visible on mobile*/ +} - #geolocate-button img { - width: 31px; - height: 31px; - margin: 6px; - } +#geolocate-button img { + width: 31px; + height: 31px; + margin: 6px; +} - #geolocate-button > .uielement { - display: block; - } +#geolocate-button > .uielement { + display: block; +} - #help-button-mobile { - display: none; - } +#help-button-mobile { + display: none; +} - /**************** GENERIC ****************/ +#layer-selection { + position: absolute; + bottom: 1em; + left: 1em; + z-index: 9000; + background-color: white; + border-radius: 1em; + cursor: pointer; +} + +/**************** GENERIC ****************/ - .alert { - background-color: #fee4d1; - font-weight: bold; - border-radius: 1em; - padding: 0.3em; - margin: 0.25em; +.alert { + background-color: #fee4d1; + font-weight: bold; + border-radius: 1em; + padding: 0.3em; + margin: 0.25em; text-align: center; padding-top: 0.15em; padding-bottom: 0.15em; @@ -335,111 +345,6 @@ pointer-events: all; } -/* filter ui */ - -.filter__popup { - position: absolute; - bottom: 0; - z-index: 500; - padding-left: 10px; - padding-bottom: 10px; -} - -.filter__button { - outline: none; - border: none; - width: 60px; - height: 60px; - border-radius: 50%; - background-color: white; - position: relative; -} - -.filter__button svg { - vertical-align: middle; -} - -#filter__selection{ -} - -#filter__selection form { - display: flex; - flex-flow: column; - width: 100%; - background-color: #ffffff; - border-radius: 15px; - border-bottom-left-radius: 30px; - border: none; - font-size: 16px; - transform: translateY(60px); - padding: 15px 0 60px 0; -} - -#filter__selection label { - font-size: 16px; - background-color: #ffffff; - padding: 0 15px 12px 15px; - margin: 0; - color: #003B8B; - font-weight: 600; -} - - -#filter__selection select { - outline: none; - background-color: #F0EFEF; - border: none; - border-radius: 5px; - font-size: 14px; - padding: 5px; - margin: 0 15px; - max-width: 250px; -} - -#filter__selection ul { - background-color: #ffffff; - padding: 10px 25px 18px 18px; - list-style: none; - margin: 0; - font-weight: 600; - transform: translateY(75px); - max-height: calc(50vh - 10em); - overflow-y: auto; -} - -#filter__selection ul li span > span { - display: flex; - align-items: center; -} - -#filter__selection ul svg { - padding: 10px 14px 10px 0; - border-right: 1px solid #003B8B; -} - -#filter__selection ul img { - width: 20px; - height: auto; - margin: 0 10px 0 18px; -} - -#filter__selection ul svg { - width: 20px; - height: auto; - margin: 0 10px 0 18px; -} - -.filter__label { - font-size: 16px; - transform: translateY(75px); - background-color: #ffffff; - padding: 10px 15px; - margin: 0; - color: #003B8B; - font-weight: 600; - border-radius: 15px 15px 0 0; -} - #centermessage { position: absolute; @@ -769,57 +674,3 @@ .add-ui { font-size: large; } - - .custom-layer-panel-header { - display: flex; - flex-wrap: nowrap; - flex-direction: row; - font-size: large; - margin: 0.5em; - background-color: white; - justify-content: flex-start; - align-items: center; - text-decoration: none; - color: black; - } - - .custom-layer-panel-header-img img { - max-width: 3em; - max-height: 3em; - padding: 0.5em; - } - - .custom-layer-panel-header-img { - min-width: 4em; - height: 4em; - -} - -.custom-layer-checkbox { - font-size: larger; - min-height: 2em; - background-color: #e5f5ff; - margin:0.3em; - display: flex; - justify-content: flex-start; - align-items: stretch; - text-decoration: none; - padding: 0.5em; - border-radius: 1em; - width: unset; -} -.custom-layer-checkbox img { - max-width: 1.5em; - max-height: 1.5em; - width: 100%; - height: 100%; - padding: 0.2em; - padding-right: 0.5em; -} - -.custom-layer-checkbox svg { - max-width: 1.5em; - max-height: 1.5em; - padding: 0.2em; - padding-right: 0.5em; -} \ No newline at end of file diff --git a/index.html b/index.html index b05b56a..0166eed 100644 --- a/index.html +++ b/index.html @@ -50,8 +50,7 @@
    -
    -
    +
    Loading MapComplete, hang on...
    diff --git a/test.ts b/test.ts index 289e449..feea3a4 100644 --- a/test.ts +++ b/test.ts @@ -1,13 +1,4 @@ -import {ImageCarousel} from "./UI/Image/ImageCarousel"; import {UIEventSource} from "./Logic/UIEventSource"; -import {OsmConnection} from "./Logic/Osm/OsmConnection"; +import DeleteImage from "./UI/Image/DeleteImage"; -const connection = new OsmConnection(true, new UIEventSource(undefined), "qsdf"); -connection.AttemptLogin(); - - -const imageCarousel = new ImageCarousel(new UIEventSource({ - "image": "https://i.imgur.com/kX3rl3v.jpg" -}), connection); - -imageCarousel.AttachTo("maindiv") \ No newline at end of file +new DeleteImage("image", new UIEventSource({"image":"url"})).AttachTo("maindiv"); \ No newline at end of file