From 0fe6b67976908f9dcb876e46f171f0cec6a7469f Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 8 Jul 2020 11:23:36 +0200 Subject: [PATCH] Add image delete button --- Customizations/Layers/Park.ts | 17 ++++++- Customizations/Layouts/BikePumps.ts | 6 +-- Customizations/TagRendering.ts | 15 ++++-- Logic/ImageSearcher.ts | 20 +++----- README.md | 3 +- UI/ConfirmDialog.ts | 69 +++++++++++++++++++++++++ UI/Image/ImageCarousel.ts | 39 +++++++++------ UI/UIElement.ts | 11 ++++ UI/UIEventSource.ts | 14 ++++-- assets/delete.svg | 55 ++++++++++++++++++++ index.css | 78 +++++++++++++++++++++++++++++ test.html | 2 + test.ts | 28 ++++++----- 13 files changed, 303 insertions(+), 54 deletions(-) create mode 100644 UI/ConfirmDialog.ts create mode 100644 assets/delete.svg diff --git a/Customizations/Layers/Park.ts b/Customizations/Layers/Park.ts index 3736435..e46dc25 100644 --- a/Customizations/Layers/Park.ts +++ b/Customizations/Layers/Park.ts @@ -9,6 +9,18 @@ import {NameInline} from "../Questions/NameInline"; export class Park extends LayerDefinition { + + private accessByDefault = new TagRenderingOptions({ + question: "Is dit park publiek toegankelijk?", + mappings: [ + {k: new Tag("access","yes"), txt: "Publiek toegankelijk"}, + {k: new Tag("access",""), txt: "Publiek toegankelijk"}, + {k: new Tag("access","no"), txt: "Niet-publiek toegankelijk park"}, + {k: new Tag("access","guided"), txt: "Enkel toegankelijk met een gids of op een activiteit"} + ] + }) + + constructor() { super(); this.name = "park"; @@ -22,7 +34,10 @@ export class Park extends LayerDefinition { this.minzoom = 13; this.style = this.generateStyleFunction(); this.title = new NameInline("park"); - this.elementsToShow = [new NameQuestion()]; + this.elementsToShow = [new NameQuestion(), + this.accessByDefault + + ]; } diff --git a/Customizations/Layouts/BikePumps.ts b/Customizations/Layouts/BikePumps.ts index a797305..a3902fb 100644 --- a/Customizations/Layouts/BikePumps.ts +++ b/Customizations/Layouts/BikePumps.ts @@ -6,16 +6,16 @@ export class BikePumpsLayout extends Layout { constructor() { super( "pomp", - "Grb import fix tool", + "Cyclofix", [new BikePumps()], 15, 51.2083, 3.2279, - "

GRB Fix tool

\n" + + "

Open CycloFix

\n" + "\n" + - "Expert use only" + "Something something bikes" , "", ""); diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index 8c11961..f94b112 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -65,7 +65,6 @@ export class TagRenderingOptions { mappings?: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[] }) { this.options = options; - } @@ -150,6 +149,7 @@ export class TagRendering extends UIElement { // Prepare the choices for the Radio buttons let i = 0; const choices: UIElement[] = []; + const alreadyUsedTexts: string[] = []; for (const choice of options.mappings ?? []) { if (choice.k === null) { @@ -159,16 +159,21 @@ export class TagRendering extends UIElement { let choiceSubbed = choice; if (choice.substitute) { choiceSubbed = { - k : choice.k.substituteValues( + k: choice.k.substituteValues( options.tagsPreprocessor(this._source.data)), - txt : this.ApplyTemplate(choice.txt), + txt: this.ApplyTemplate(choice.txt), substitute: false, priority: choice.priority } } - - choices.push(new FixedUiElement(choiceSubbed.txt)); + const txt = choiceSubbed.txt; + if (alreadyUsedTexts.indexOf(txt) < 0) { + choices.push(new FixedUiElement(txt)); + alreadyUsedTexts.push(txt); + } + + this._mapping.push(choiceSubbed); i++; } diff --git a/Logic/ImageSearcher.ts b/Logic/ImageSearcher.ts index 7e7b5dd..dc48153 100644 --- a/Logic/ImageSearcher.ts +++ b/Logic/ImageSearcher.ts @@ -105,9 +105,8 @@ export class ImageSearcher extends UIEventSource { if(key === undefined){ return; } - console.log("Deleting image..."); - - // this._changes.addChange(this._tags.data.id, key, ""); + console.log("Deleting image...", key, " --> ", url); + this._changes.addChange(this._tags.data.id, key, ""); } @@ -133,16 +132,11 @@ export class ImageSearcher extends UIEventSource { } } - const image0 = this._tags.data["image:0"]; - if (image0 !== undefined) { - this.AddImage(image0); - } - let imageIndex = 1; - let imagei = this._tags.data["image:" + imageIndex]; - while (imagei !== undefined) { - this.AddImage(imagei); - imageIndex++; - imagei = this._tags.data["image:" + imageIndex]; + for (const key in this._tags.data) { + // @ts-ignore + if(key.startsWith("image:")){ + this.AddImage(this._tags.data[key]); + } } const wdItem = this._tags.data.wikidata; diff --git a/README.md b/README.md index 5cd3832..448373b 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ Camera Icon, Dave Gandy, CC-BY-SA 3.0 https://commons.wikimedia.org/wiki/File:OOjs_UI_indicator_search-rtl.svg Search Icon, MIT - +https://commons.wikimedia.org/wiki/File:Trash_font_awesome.svg +Trash icon by Dave Gandy, CC-BY-SA https://commons.wikimedia.org/wiki/File:Home-icon.svg Home icon by Timothy Miller, CC-BY-SA 3.0 diff --git a/UI/ConfirmDialog.ts b/UI/ConfirmDialog.ts new file mode 100644 index 0000000..8395401 --- /dev/null +++ b/UI/ConfirmDialog.ts @@ -0,0 +1,69 @@ +import {UIElement} from "./UIElement"; +import {UIEventSource} from "./UIEventSource"; +import {FixedUiElement} from "./Base/FixedUiElement"; +import {VariableUiElement} from "./Base/VariableUIElement"; + + +export class ConfirmDialog extends UIElement { + private _showOptions: UIEventSource = new UIEventSource(false); + + private _question: UIElement; + private _optionA: UIElement; + private _optionB: UIElement; + + constructor( + show: UIEventSource, + question: string, + optionA: string, optionB: string, + executeA: () => void, + executeB: () => void, + classA: string = "", + classB: string = "") { + super(show); + this.ListenTo(this._showOptions); + const self = this; + show.addCallback(() => { + self._showOptions.setData(false); + }) + this._question = new FixedUiElement("" + question + "") + .onClick(() => { + self._showOptions.setData(!self._showOptions.data); + }); + this._optionA = new VariableUiElement( + this._showOptions.map( + (show) => show ? "
" + optionA + "
" : "")) + .onClick(() => { + self._showOptions.setData(false); + executeA(); + } + ); + this._optionB = new VariableUiElement( + this._showOptions.map((show) => + show ? "
" + optionB + "
" : "") ) + .onClick(() => { + self._showOptions.setData(false); + executeB(); + }); + + + + } + + protected InnerRender(): string { + if (!this._source.data) { + return ""; + } + + return this._question.Render() + + this._optionA.Render() + + this._optionB.Render(); + } + + Update() { + super.Update(); + this._question.Update(); + this._optionA.Update(); + this._optionB.Update(); + } + +} \ No newline at end of file diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index 05682c4..21d36f6 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -6,6 +6,7 @@ import {FixedUiElement} from "../Base/FixedUiElement"; import {VerticalCombine} from "../Base/VerticalCombine"; import {Changes} from "../../Logic/Changes"; import {VariableUiElement} from "../Base/VariableUIElement"; +import {ConfirmDialog} from "../ConfirmDialog"; export class ImageCarousel extends UIElement { /** @@ -25,7 +26,6 @@ export class ImageCarousel extends UIElement { private readonly _uiElements: UIEventSource; - private readonly _deleteButtonText = new UIEventSource(""); private readonly _deleteButton: UIElement; constructor(tags: UIEventSource, changes: Changes) { @@ -48,24 +48,33 @@ export class ImageCarousel extends UIElement { new FixedUiElement("")).HideOnEmpty(true); - this._deleteButtonText = this.slideshow._currentSlide.map((i) => { - if(self.searcher.IsDeletable(self.searcher.data[i])){ - return "DELETE"; - }else{ - return ""; - } - }); + const showDeleteButton = this.slideshow._currentSlide.map((i) => { + return self.searcher.IsDeletable(self.searcher.data[i]); + }, [this.searcher]); + this.slideshow._currentSlide.addCallback(() => { + showDeleteButton.ping(); // This pings the showDeleteButton, which indicates that it has to hide it's subbuttons + }) - this._deleteButton = new VariableUiElement(this._deleteButtonText) - .HideOnEmpty(true) - .onClick(() => { - self.searcher.Delete(self.searcher.data[self.slideshow._currentSlide.data]); - }); + + const deleteCurrent = () => self.searcher.Delete(self.searcher.data[self.slideshow._currentSlide.data]); + + this._deleteButton = new ConfirmDialog(showDeleteButton, + "Afbeelding verwijderen", + "Afbeelding verwijderen", + "Terug", + deleteCurrent, + () => {}, + 'delete-image-confirm', + 'delete-image-cancel'); } InnerRender(): string { - return this.slideshow.Render() ; - // + this._deleteButton.Render(); + return "" + + "
" + + this._deleteButton.Render() + + "
" + + this.slideshow.Render() + + "
"; } InnerUpdate(htmlElement: HTMLElement) { diff --git a/UI/UIElement.ts b/UI/UIElement.ts index 5a72043..57f1427 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -58,6 +58,17 @@ export abstract class UIElement { } element.style.pointerEvents = "all"; element.style.cursor = "pointer"; + /* + const childs = element.children; + for (let i = 0; i < childs.length; i++) { + const ch = childs[i]; + console.log(ch); + ch.style.cursor = "pointer"; + ch.onclick = () => { + self._onClick(); + } + ch.style.pointerEvents = "all"; + }*/ } this.InnerUpdate(element); diff --git a/UI/UIEventSource.ts b/UI/UIEventSource.ts index f1a9909..eae2151 100644 --- a/UI/UIEventSource.ts +++ b/UI/UIEventSource.ts @@ -27,15 +27,23 @@ export class UIEventSource{ } } - public map(f: ((T) => J)): UIEventSource { + public map(f: ((T) => J), + extraSources : UIEventSource[] = []): UIEventSource { const self = this; - this.addCallback(function () { + + const update = function () { newSource.setData(f(self.data)); newSource.ping(); - }); + } + + this.addCallback(update); + for (const extraSource of extraSources) { + extraSource.addCallback(update); + } const newSource = new UIEventSource( f(this.data) ); + return newSource; diff --git a/assets/delete.svg b/assets/delete.svg new file mode 100644 index 0000000..60bf193 --- /dev/null +++ b/assets/delete.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/index.css b/index.css index f95ddb0..f61ecac 100644 --- a/index.css +++ b/index.css @@ -682,6 +682,84 @@ body { display: inline-block } +/******* THe remove image buttons ****/ + +.image-carousel-container { + position: relative; +} + +.image-delete-container { + position: absolute; + left: 6em; + top: 1.5em; + display: inline-block; + z-index: 7000; + +} + +.delete-image { + width: 1.5em; + height: 1.5em; + padding: 0.5em; + border-radius: 3em; + background-color: black; +} + +.delete-image-confirm { + position: absolute; + display: inline-block; + left: 0; + top: 2.5em; + + padding: 0.5em; + padding-left: 0.75em; + + z-index: -1; + height: 3em; + width: 14em; + border-radius: 1em; + border-top-left-radius: 0; + border-top-right-radius: 0; + background-color: #ff8c8c; + + color: white; + height: 1.5em; /* same as .delete-image */ + + z-index: 7000; +} + +.delete-image-confirm span { + font-size: larger; + font-weight: bold; +} + + +.delete-image-cancel { + display: inline-block; + position: absolute; + + left: 0em; + padding: 0.5em; + padding-left: 0.75em; + + border-radius: 1em; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + + height: 1.5em; /* same as .delete-image */ + width: 14em; /* Same as delete-image-confirm */ + + + background-color: black; + color: white; + z-index: 7000; +} + +.delete-image-cancel span { + font-size: larger; + font-weight: bold; + +} /**** The save button *****/ diff --git a/test.html b/test.html index b5f19fb..ad22b5d 100644 --- a/test.html +++ b/test.html @@ -5,7 +5,9 @@ +
'maindiv' not attached
+
'extradiv' not attached
diff --git a/test.ts b/test.ts index 9079435..f006400 100644 --- a/test.ts +++ b/test.ts @@ -4,18 +4,20 @@ import {OsmConnection} from "./Logic/OsmConnection"; import {ElementStorage} from "./Logic/ElementStorage"; import {WikipediaLink} from "./Customizations/Questions/WikipediaLink"; import {OsmLink} from "./Customizations/Questions/OsmLink"; - -const tags = {name: "Test", - wikipedia: "nl:Pieter", - id: "node/-1"}; -const tagsES = new UIEventSource(tags); - -const login = new OsmConnection(true); - -const allElements = new ElementStorage(); -allElements.addElementById(tags.id, tagsES); - -const changes = new Changes("Test", login, allElements) +import {ConfirmDialog} from "./UI/ConfirmDialog"; -new OsmLink(tagsES, changes).AttachTo("maindiv"); \ No newline at end of file +new ConfirmDialog(new UIEventSource(true), + "Afbeelding verwijderen", + "Deze afbeelding verwijderen", + "Terug", + + () => { + console.log("Verwijderen"); + }, + () => { + console.log("terug") + }, + 'delete-image-confirm', + 'delete-image-cancel') + .AttachTo("maindiv") \ No newline at end of file