From 5b59d7dbd07102da810f42f10f496b6bc3b9650b Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 12 Nov 2020 12:18:02 +0100 Subject: [PATCH 01/20] Fixed small bugs, add documentation to query parameters, draft of surveillance cams --- Logic/Web/QueryParameters.ts | 2 +- State.ts | 11 +- UI/Input/MultiInput.ts | 8 +- UI/Popup/FeatureInfoBox.ts | 3 +- assets/layers/surveillance_cameras.json | 228 ++++++++++++++++++++++++ index.css | 5 + 6 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 assets/layers/surveillance_cameras.json diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts index 21a57cc1c..235ff7b61 100644 --- a/Logic/Web/QueryParameters.ts +++ b/Logic/Web/QueryParameters.ts @@ -59,7 +59,7 @@ export class QueryParameters { } - public static GetQueryParameter(key: string, deflt: string): UIEventSource { + public static GetQueryParameter(key: string, deflt: string, documentation?: string): UIEventSource { if(!this.initialized){ this.init(); } diff --git a/State.ts b/State.ts index 19e041a85..584301886 100644 --- a/State.ts +++ b/State.ts @@ -165,7 +165,7 @@ export default class State { }); - function featSw(key: string, deflt: (layout: LayoutConfig) => boolean): UIEventSource { + function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation?: string): UIEventSource { const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined); // I'm so sorry about someone trying to decipher this @@ -173,13 +173,14 @@ export default class State { return UIEventSource.flatten( self.layoutToUse.map((layout) => { const defaultValue = deflt(layout); - const queryParam = QueryParameters.GetQueryParameter(key, "" + defaultValue) + const queryParam = QueryParameters.GetQueryParameter(key, "" + defaultValue, documentation) return queryParam.map((str) => str === undefined ? defaultValue : (str !== "false")); }), [queryParameterSource]); } - this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true); + this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true, + "Disables the userbadge (and thus disables login capabilities)"); this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true); this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true); this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true); @@ -213,8 +214,10 @@ export default class State { continue; } try { + const json = btoa(customLayout.data); + console.log(json); const layout = new LayoutConfig( - JSON.parse(btoa(customLayout.data))); + JSON.parse(json)); installedThemes.push({ layout: layout, definition: customLayout.data diff --git a/UI/Input/MultiInput.ts b/UI/Input/MultiInput.ts index 22f9fdf6a..3e4048058 100644 --- a/UI/Input/MultiInput.ts +++ b/UI/Input/MultiInput.ts @@ -71,7 +71,7 @@ export class MultiInput extends InputElement { input.IsSelected.addCallback(() => this.UpdateIsSelected()); const moveUpBtn = Svg.up_ui() - .onClick(() => { + .SetClass('small-image').onClick(() => { const v = self._value.data[i]; self._value.data[i] = self._value.data[i - 1]; self._value.data[i - 1] = v; @@ -79,8 +79,8 @@ export class MultiInput extends InputElement { }); const moveDownBtn = - Svg.down_ui().SetStyle('max-width: 1.5em; margin-left: 5px;display:block;') - .onClick(() => { + Svg.down_ui() + .SetClass('small-image') .onClick(() => { const v = self._value.data[i]; self._value.data[i] = self._value.data[i + 1]; self._value.data[i + 1] = v; @@ -98,7 +98,7 @@ export class MultiInput extends InputElement { const deleteBtn = - Svg.delete_icon_ui().SetStyle('max-width: 1.5em;width:1.5em; margin-left: 5px;') + Svg.delete_icon_ui().SetClass('small-image') .onClick(() => { self._value.data.splice(i, 1); self._value.ping(); diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index c0439c266..ddf23a304 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -5,6 +5,7 @@ import EditableTagRendering from "./EditableTagRendering"; import QuestionBox from "./QuestionBox"; import Combine from "../Base/Combine"; import TagRenderingAnswer from "./TagRenderingAnswer"; +import State from "../../State"; export class FeatureInfoBox extends UIElement { private _tags: UIEventSource; @@ -33,8 +34,6 @@ export class FeatureInfoBox extends UIElement { layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon))) .SetClass("featureinfobox-icons"); this._renderings = layerConfig.tagRenderings.map(tr => new EditableTagRendering(tags, tr)); - this._questionBox = new QuestionBox(tags, layerConfig.tagRenderings); - } InnerRender(): string { diff --git a/assets/layers/surveillance_cameras.json b/assets/layers/surveillance_cameras.json new file mode 100644 index 000000000..46126a56f --- /dev/null +++ b/assets/layers/surveillance_cameras.json @@ -0,0 +1,228 @@ +{ + "id": "surveillance", + "title": { + "en": "Surveillance under Surveillance", + "nl": "Surveillance under Surveillance" + }, + "shortDescription": { + "en": "Surveillance cameras and other means of surveillance", + "nl": "Bewakingscameras en dergelijke" + }, + "description": { + "en": "On this open map, you can find surveillance cameras.", + "nl": "Op deze open kaart kan je bewakingscamera's vinden." + }, + "language": [ + "en", + "nl" + ], + "maintainer": "", + "icon": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg", + "version": "0", + "startLat": 0, + "startLon": 0, + "startZoom": 1, + "widenFactor": 0.05, + "socialImage": "", + "layers": [ + { + "id": "cameras", + "name": { + "en": "Surveillance camera's", + "nl": "Bewakingscamera's" + }, + "minzoom": 12, + "overpassTags": { + "and": [ + "man_made=surveillance", + { + "or": [ + "surveillance:type=camera", + "surveillance:type=ALPR", + "surveillance:type=ANPR" + ] + } + ] + }, + "title": { + "render": { + "en": "Surveillance Camera", + "nl": "Bewakingscamera" + } + }, + "description": {}, + "tagRenderings": [ + { + "question": { + "en": "What kind of camera is this?", + "nl": "Wat voor soort camera is dit?" + }, + "mappings": [ + { + "if": { + "and": [ + "camera:type=fixed" + ] + }, + "then": { + "en": "A fixed (non-moving) camera", + "nl": "Een vaste camera" + } + }, + { + "if": { + "and": [ + "camera:type=dome" + ] + }, + "then": { + "en": "A dome camera (which can turn)", + "nl": "Een dome (bolvormige camera die kan draaien)" + } + }, + { + "if": { + "and": [ + "camera:type=panning" + ] + }, + "then": { + "en": "A panning camera", + "nl": "Een camera die (met een motor) van links naar rechts kan draaien" + } + } + ] + }, + { + "freeform": { + "key": "operator" + }, + "question": { + "en": "Who operates this CCTV?", + "nl": "Wie beheert deze bewakingscamera?" + }, + "render": { + "en": "Operated by {operator}", + "nl": "Beheer door {operator}" + } + }, + { + "question": { + "en": "What k ind of surveillance is this camera", + "nl": "Wat soort bewaking wordt hier uitgevoerd?" + }, + "mappings": [ + { + "if": { + "and": [ + "surveillance=public" + ] + }, + "then": { + "en": "A public area is surveilled, such as a street, a bridge, a square, a park, a train station...", + "nl": "Bewaking van de publieke ruilmte, dus een straat, een brug, een park, een plein, een stationsgebouw..." + } + }, + { + "if": { + "and": [ + "surveillance=outdoor" + ] + }, + "then": { + "en": "An outdoor, yet private area is surveilled (e.g. a parking lot, a fuel station, courtyard, entrance, private driveway, ...)", + "nl": "Een buitenruimte met privaat karakter (zoals een privé-oprit, een parking, tankstation, ...)" + } + }, + { + "if": { + "and": [ + "surveillance=indoor" + ] + }, + "then": { + "nl": "Een private binnenruimte wordt bewaakt, bv. een wiinkel, een parkeergarage, ...", + "en": "A private indoor area is surveilled, e.g. a shop, a private underground parking, ..." + } + } + ] + }, + { + "question": { + "en": "What exactly is surveilled here?", + "nl": "Wat wordt hier precies bewaakt?" + }, + "freeform": { + "key": "surveillance:type" + }, + "render": { + "en": " Surveills a {surveillance:type}", + "nl": "Bewaakt een {surveillance:type}" + }, + "mappings": [ + { + "if": { + "and": [ + "surveillance:zone=parking" + ] + }, + "then": { + "en": "Surveills a parking", + "nl": "Bewaakt een parking" + } + }, + { + "if": { + "and": [ + "surveillance:zone=traffic" + ] + }, + "then": { + "en": "Surveills the traffic", + "nl": "Bewaakt het verkeer" + } + }, + { + "if": { + "and": [ + "surveillance:zone=entrance" + ] + }, + "then": { + "en": "Surveills an entrance", + "nl": "Bewaakt een ingang" + } + }, + { + "if": { + "and": [ + "surveillance:zone=shop" + ] + }, + "then": { + "en": "Surveills a shop", + "nl": "Bewaakt een winkel" + } + } + ] + } + ], + "hideUnderlayingFeaturesMinPercentage": 0, + "icon": { + "render": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg" + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "30,30,center" + }, + "color": { + "render": "#00f" + }, + "presets": [], + "wayHandling": 2 + } + ], + "roamingRenderings": [] +} \ No newline at end of file diff --git a/index.css b/index.css index 135578abb..8df3e9aba 100644 --- a/index.css +++ b/index.css @@ -416,3 +416,8 @@ body { } +.small-image img { + height: 1em; + max-width: 1em; +} + From ba44024dd91baec2ddfdc4574026bc0ee92247b9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 13 Nov 2020 23:58:11 +0100 Subject: [PATCH 02/20] Fixes, surveillance cams v0.1 --- Customizations/AllKnownLayouts.ts | 6 ++- InitUiElements.ts | 4 +- Logic/Web/QueryParameters.ts | 23 +++++++--- README.md | 46 ++++++++++++++++++- State.ts | 46 +++++++++++-------- UI/Image/DeleteImage.ts | 3 ++ UI/Image/ImageUploadFlow.ts | 4 ++ UI/Popup/TagRenderingAnswer.ts | 24 ++++------ .../surveillance_cameras.json | 26 ++++++++++- index.ts | 20 +------- 10 files changed, 137 insertions(+), 65 deletions(-) rename assets/{layers => themes/surveillance_cameras}/surveillance_cameras.json (89%) diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index e00ca6c21..5c1c0a02e 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -15,10 +15,11 @@ import * as benches from "../assets/themes/benches/benches.json"; import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json" import * as widths from "../assets/themes/widths/width.json" import * as drinking_water from "../assets/themes/drinking_water/drinking_water.json" -import LayerConfig from "./JSON/LayerConfig"; -import SharedLayers from "./SharedLayers"; +import * as surveillance_cameras from "../assets/themes/surveillance_cameras/surveillance_cameras.json" import * as personal from "../assets/themes/personalLayout/personalLayout.json" +import LayerConfig from "./JSON/LayerConfig"; import LayoutConfig from "./JSON/LayoutConfig"; +import SharedLayers from "./SharedLayers"; export class AllKnownLayouts { @@ -60,6 +61,7 @@ export class AllKnownLayouts { new LayoutConfig(widths), new LayoutConfig(buurtnatuur), new LayoutConfig(bike_monitoring_stations), + new LayoutConfig(surveillance_cameras) ]; diff --git a/InitUiElements.ts b/InitUiElements.ts index 4b9a85503..8310b0c71 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -442,7 +442,7 @@ export class InitUiElements { State.state.layerUpdater = new UpdateFromOverpass(State.state); State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state).availableEditorLayers; - const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId); + const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId, "The id of the background layer to start with"); queryParam.addCallbackAndRun((selectedId: string) => { const available = State.state.availableBackgroundLayers.data; @@ -483,7 +483,7 @@ export class InitUiElements { const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo); flayers.push(flayer); - QueryParameters.GetQueryParameter("layer-" + layer.id, "true") + QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer "+layer.id+" is shown") .map((str) => str !== "false", [], (b) => b.toString()) .syncWith( flayer.isDisplayed diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts index 235ff7b61..821c163c2 100644 --- a/Logic/Web/QueryParameters.ts +++ b/Logic/Web/QueryParameters.ts @@ -5,20 +5,22 @@ import {UIEventSource} from "../UIEventSource"; export class QueryParameters { - private static order: string [] = ["layout","test","z","lat","lon"]; + private static order: string [] = ["layout", "test", "z", "lat", "lon"]; private static knownSources = {}; private static initialized = false; private static defaults = {} - - private static addOrder(key){ - if(this.order.indexOf(key) < 0){ + + private static documentation = {} + + private static addOrder(key) { + if (this.order.indexOf(key) < 0) { this.order.push(key) } } private static init() { - - if(this.initialized){ + + if (this.initialized) { return; } this.initialized = true; @@ -63,6 +65,7 @@ export class QueryParameters { if(!this.initialized){ this.init(); } + QueryParameters.documentation[key] = documentation; if (deflt !== undefined) { QueryParameters.defaults[key] = deflt; } @@ -76,4 +79,12 @@ export class QueryParameters { return source; } + public static GenerateQueryParameterDocs(): string { + const docs = []; + for (const key in QueryParameters.documentation) { + docs.push("**" + key + "**: " + QueryParameters.documentation[key] + " (default value: _" + QueryParameters.defaults[key] + "_)") + } + return docs.join("\n\n"); + } + } \ No newline at end of file diff --git a/README.md b/README.md index 3f527120b..1a2eed364 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ A theme has translations into the preset.json (`assets/themes/themename/themenam 1. Modify `"language"` to contain the new language, e.g. `"language": "nl"` becomes `"language": ["nl", "en"]` 2. Add extra strings to the texts. If it used to be a single-language theme, one can replace the strings, e.g.: `"description": "Welcome to Open Bookcase Map"` to `"description": {"en": "Welcome to Open Bookcase Map", "nl": "Welkom bij de OpenBoekenruilkastenKaart", "fr": "Bienvenue sûr la carte des petites bibliotheques"}`. If the correct language is not found, it'll fallback to another supported language. -3. If you notice missing translations in the core of MapComplete, fork this project, open [the file containing all translations](https://github.com/pietervdvn/MapComplete/blob/master/UI/i18n/Translations.ts), add add a language string there +3. If you notice missing translations in the core of MapComplete, fork this project, open [the file containing all translations](https://github.com/pietervdvn/MapComplete/blob/master/assets/translations.json), add add a language string there 4. Send a pull request to update the languages, I'll gladly add it! It doesn't have to be a complete translation from the start ;) ### Adding your theme to the repository @@ -165,6 +165,50 @@ Whenever a change is made -even adding a single tag- the change is uploaded into Note that changesets are closed automatically after one hour of inactivity, so we don't have to worry about closing them. +### Query parameters + +By adding extra query parameters, more options are available to influence: + +**test**: If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org (default value: _false_) + +**layout**: The layout to load into MapComplete (default value: _bookcases_) + +**userlayout**: undefined (default value: _false_) + +**layer-control-toggle**: Wether or not the layer control is shown (default value: _false_) + +**tab**: The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >200 changesets) (default value: _0_) + +**z**: The initial/current zoom level (default value: _1_) + +**lat**: The initial/current latitude (default value: _0_) + +**lon**: The initial/current longitude of the app (default value: _0_) + +**fs-userbadge**: Disables/Enables the userbadge (and thus disables login capabilities) (default value: _true_) + +**fs-search**: Disables/Enables the search bar (default value: _true_) + +**fs-layers**: Disables/Enables the layer control (default value: _true_) + +**fs-add-new**: Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) (default value: _true_) + +**fs-welcome-message**: undefined (default value: _true_) + +**fs-iframe**: Disables/Enables the iframe-popup (default value: _false_) + +**fs-more-quests**: Disables/Enables the 'More Quests'-tab in the welcome message (default value: _true_) + +**fs-share-screen**: Disables/Enables the 'Share-screen'-tab in the welcome message (default value: _true_) + +**fs-geolocation**: Disables/Enables the geolocation button (default value: _true_) + +**oauth_token**: Used to complete the login (default value: _undefined_) + +**background**: The id of the background layer to start with (default value: _undefined_) + +**layer-bookcases**: Wehter or not layer bookcases is shown (default value: _true_) index.ts:104:8 + # Privacy Privacy is important, we try to leak as little information as possible. diff --git a/State.ts b/State.ts index 0bbb77925..5d7b63fba 100644 --- a/State.ts +++ b/State.ts @@ -115,10 +115,10 @@ export default class State { public layoutDefinition: string; public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>; - public layerControlIsOpened: UIEventSource = QueryParameters.GetQueryParameter("layer-control-toggle", "false") + public layerControlIsOpened: UIEventSource = QueryParameters.GetQueryParameter("layer-control-toggle", "false", "Wether or not the layer control is shown") .map((str) => str !== "false", [], b => "" + b) - public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0").map( + public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0", `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${State.userJourney.mapCompleteHelpUnlock} changesets)`).map( str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n ); @@ -138,11 +138,11 @@ export default class State { }) } this.zoom = asFloat( - QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom) + QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom, "The initial/current zoom level") .syncWith(LocalStorageSource.Get("zoom"))); - this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat) + this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat, "The initial/current latitude") .syncWith(LocalStorageSource.Get("lat"))); - this.lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon) + this.lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon, "The initial/current longitude of the app") .syncWith(LocalStorageSource.Get("lon"))); @@ -165,8 +165,8 @@ export default class State { }); - function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation?: string): UIEventSource { - const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined); + function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource { + const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation); // I'm so sorry about someone trying to decipher this // It takes the current layout, extracts the default value for this query paramter. A query parameter event source is then retreived and flattened @@ -180,20 +180,30 @@ export default class State { this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true, - "Disables the userbadge (and thus disables login capabilities)"); - this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true); - this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true); - this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true); - this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true); - this.featureSwitchIframe = featSw("fs-iframe", () => false); - this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true); - this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true); - this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true); + "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode."); + this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true, + "Disables/Enables the search bar"); + this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true, + "Disables/Enables the layer control"); + this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, + "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)"); + this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true, + "Disables/enables the help menu or welcome message"); + this.featureSwitchIframe = featSw("fs-iframe", () => false, + "Disables/Enables the iframe-popup"); + this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, + "Disables/Enables the 'More Quests'-tab in the welcome message"); + this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true, + "Disables/Enables the 'Share-screen'-tab in the welcome message"); + this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true, + "Disables/Enables the geolocation button"); - const testParam = QueryParameters.GetQueryParameter("test", "false").data; + const testParam = QueryParameters.GetQueryParameter("test", "false", + "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org").data; this.osmConnection = new OsmConnection( testParam === "true", - QueryParameters.GetQueryParameter("oauth_token", undefined), + QueryParameters.GetQueryParameter("oauth_token", undefined, + "Used to complete the login"), layoutToUse.id, true ); diff --git a/UI/Image/DeleteImage.ts b/UI/Image/DeleteImage.ts index b59af0603..7e5a2c1bb 100644 --- a/UI/Image/DeleteImage.ts +++ b/UI/Image/DeleteImage.ts @@ -42,6 +42,9 @@ export default class DeleteImage extends UIElement { } InnerRender(): string { + if(!State.state.featureSwitchUserbadge.data){ + return ""; + } const value = this.tags.data[this.key]; if (value === undefined || value === "") { diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 0e8815a39..2fb61bc8d 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -51,6 +51,10 @@ export class ImageUploadFlow extends UIElement { } InnerRender(): string { + + if(!State.state.featureSwitchUserbadge.data){ + return ""; + } const t = Translations.t.image; if (State.state.osmConnection.userDetails === undefined) { diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index 8c9f6ddb7..f560bf293 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -9,25 +9,11 @@ import {SubstitutedTranslation} from "../SpecialVisualizations"; export default class TagRenderingAnswer extends UIElement { private _tags: UIEventSource; private _configuration: TagRenderingConfig; - private _content: UIElement; constructor(tags: UIEventSource, configuration: TagRenderingConfig) { super(tags); this._tags = tags; this._configuration = configuration; - const self = this; - tags.addCallbackAndRun(tags => { - if (tags === undefined) { - self._content = undefined - return; - } - const tr = this._configuration.GetRenderValue(tags); - if (tr === undefined) { - self._content = undefined - return - } - self._content = new SubstitutedTranslation(tr, self._tags) - }) } InnerRender(): string { @@ -36,10 +22,16 @@ export default class TagRenderingAnswer extends UIElement { return ""; } } - if(this._content === undefined){ + + const tags = this._tags.data; + if (tags === undefined) { return ""; } - return this._content.Render(); + const tr = this._configuration.GetRenderValue(tags); + if (tr === undefined) { + return ""; + } + return new SubstitutedTranslation(tr, this._tags).Render(); } } \ No newline at end of file diff --git a/assets/layers/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json similarity index 89% rename from assets/layers/surveillance_cameras.json rename to assets/themes/surveillance_cameras/surveillance_cameras.json index 46126a56f..c2af520cf 100644 --- a/assets/layers/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -156,8 +156,8 @@ "key": "surveillance:type" }, "render": { - "en": " Surveills a {surveillance:type}", - "nl": "Bewaakt een {surveillance:type}" + "en": " Surveills a {surveillance:zone}", + "nl": "Bewaakt een {surveillance:zone}" }, "mappings": [ { @@ -193,6 +193,28 @@ "nl": "Bewaakt een ingang" } }, + { + "if": { + "and": [ + "surveillance:zone=corridor" + ] + }, + "then": { + "en": "Surveills a corridor", + "nl": "Bewaakt een gang" + } + }, + { + "if": { + "and": [ + "surveillance:zone=public_transport_platform" + ] + }, + "then": { + "en": "Surveills a public tranport platform", + "nl": "Bewaakt een perron of bushalte" + } + }, { "if": { "and": [ diff --git a/index.ts b/index.ts index 712f57072..62b53f833 100644 --- a/index.ts +++ b/index.ts @@ -4,7 +4,6 @@ import {InitUiElements} from "./InitUiElements"; import {QueryParameters} from "./Logic/Web/QueryParameters"; import {UIEventSource} from "./Logic/UIEventSource"; import * as $ from "jquery"; -import SharedLayers from "./Customizations/SharedLayers"; import LayoutConfig from "./Customizations/JSON/LayoutConfig"; let defaultLayout = "bookcases" @@ -54,23 +53,7 @@ if (path !== "index.html" && path !== "") { defaultLayout = path.substr(0, path.length - 5); console.log("Using layout", defaultLayout); } - -// Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default -for (const k in AllKnownLayouts.allSets) { - const layout : LayoutConfig= AllKnownLayouts.allSets[k]; - const possibleParts = (layout.locationContains ?? []); - for (const locationMatch of possibleParts) { - if (locationMatch === "") { - continue - } - if (window.location.href.toLowerCase().indexOf(locationMatch.toLowerCase()) >= 0) { - defaultLayout = layout.name; - } - } -} - -defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout).data; - +defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout,"The layout to load into MapComplete").data; let layoutToUse: LayoutConfig = AllKnownLayouts.allSets[defaultLayout.toLowerCase()] ?? AllKnownLayouts["all"]; @@ -118,3 +101,4 @@ if (layoutFromBase64.startsWith("wiki:")) { InitUiElements.InitAll(layoutToUse, layoutFromBase64, testing, defaultLayout); } +// console.log(QueryParameters.GenerateQueryParameterDocs()) From 3fdb84e481c222822ab8e957042fab418ca3c7f1 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 14 Nov 2020 02:54:33 +0100 Subject: [PATCH 03/20] Extract variables from html, add black theme to surveillance cameras, use svgs directly in the frontend --- InitUiElements.ts | 24 ++-- Svg.ts | 109 ++++++++++++------ UI/FullScreenMessageBoxHandler.ts | 3 +- UI/Image/ImageUploadFlow.ts | 6 +- UI/Input/Direction.ts | 39 +++++++ UI/Popup/EditableTagRendering.ts | 2 +- UI/UserBadge.ts | 12 +- assets/svg/ampersand.svg | 2 +- assets/svg/checkmark.svg | 2 +- assets/svg/envelope.svg | 51 +++++++- assets/svg/gear.svg | 5 +- assets/svg/help.svg | 22 +--- assets/svg/home.svg | 5 +- assets/svg/pencil.svg | 2 +- assets/svg/share.svg | 8 +- .../surveillance_cameras/custom_theme.css | 12 ++ .../surveillance_cameras.json | 3 + createLayouts.ts | 8 +- css/mobile.css | 10 +- css/openinghourstable.css | 25 ++-- css/tabbedComponent.css | 42 +++++-- css/tagrendering.css | 27 ++++- css/userbadge.css | 22 ++-- generateIncludedImages.ts | 3 + index.css | 95 +++++++++++---- test.ts | 15 +-- 26 files changed, 402 insertions(+), 152 deletions(-) create mode 100644 UI/Input/Direction.ts create mode 100644 assets/themes/surveillance_cameras/custom_theme.css diff --git a/InitUiElements.ts b/InitUiElements.ts index 8310b0c71..faa8dbbf3 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -110,6 +110,16 @@ export class InitUiElements { InitUiElements.setupAllLayerElements(); + if (layoutToUse.customCss !== undefined) { + var head = document.getElementsByTagName('head')[0]; + var link = document.createElement('link'); + link.id = "customCss"; + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = layoutToUse.customCss; + link.media = 'all'; + head.appendChild(link); + } function updateFavs() { const favs = State.state.favouriteLayers.data ?? []; @@ -269,7 +279,7 @@ export class InitUiElements { ] if (State.state.featureSwitchShareScreen.data) { - tabs.push({header: Svg.share_img, content: new ShareScreen()}); + tabs.push({header: Svg.share, content: new ShareScreen()}); } if (State.state.featureSwitchMoreQuests.data) { @@ -282,7 +292,7 @@ export class InitUiElements { tabs.push({ - header: Svg.help_img, + header: Svg.help , content: new VariableUiElement(State.state.osmConnection.userDetails.map(userdetails => { if (userdetails.csCount < State.userJourney.mapCompleteHelpUnlock) { return "" @@ -303,8 +313,8 @@ export class InitUiElements { const fullOptions = this.CreateWelcomePane(); - const help = Svg.help_ui().SetClass("open-welcome-button"); - const close = Svg.close_ui().SetClass("close-welcome-button"); + const help = Svg.help_svg().SetClass("open-welcome-button"); + const close = Svg.close_svg().SetClass("close-welcome-button"); const checkbox = new CheckBox( new Combine([ close, @@ -328,7 +338,7 @@ export class InitUiElements { const fullOptions2 = this.CreateWelcomePane(); State.state.fullScreenMessage.setData(fullOptions2) - Svg.help_ui() + Svg.help_svg() .SetClass("open-welcome-button") .SetClass("shadow") .onClick(() => { @@ -366,14 +376,14 @@ export class InitUiElements { } layerControlPanel.SetStyle("display:block;padding:1em;border-radius:1em;"); - const closeButton = Svg.close_ui().SetClass("layer-selection-toggle").SetStyle(" background: #e5f5ff;") + const closeButton = Svg.close_svg().SetClass("layer-selection-toggle").SetStyle(" background: var(--subtle-detail-color);") const checkbox = new CheckBox( new Combine([ closeButton, layerControlPanel]).SetStyle("display:flex;flex-direction:row;") .SetClass("hidden-on-mobile") , - Svg.layers_ui().SetClass("layer-selection-toggle"), + Svg.layers_svg().SetClass("layer-selection-toggle"), State.state.layerControlIsOpened ); diff --git a/Svg.ts b/Svg.ts index 7a112373d..a3a3de197 100644 --- a/Svg.ts +++ b/Svg.ts @@ -4,160 +4,199 @@ import {FixedUiElement} from "./UI/Base/FixedUiElement"; export default class Svg { - public static add = " image/svg+xml " + public static add = " image/svg+xml " public static add_img = Img.AsImageElement(Svg.add) + public static add_svg() { return new FixedUiElement(Svg.add);} public static add_ui() { return new FixedUiElement(Svg.add_img);} - public static addSmall = " image/svg+xml " + public static addSmall = " image/svg+xml " public static addSmall_img = Img.AsImageElement(Svg.addSmall) + public static addSmall_svg() { return new FixedUiElement(Svg.addSmall);} public static addSmall_ui() { return new FixedUiElement(Svg.addSmall_img);} - public static ampersand = " image/svg+xml " + public static ampersand = "e image/svg+xml " public static ampersand_img = Img.AsImageElement(Svg.ampersand) + public static ampersand_svg() { return new FixedUiElement(Svg.ampersand);} public static ampersand_ui() { return new FixedUiElement(Svg.ampersand_img);} - public static arrow_left_smooth = " image/svg+xml " + public static arrow_left_smooth = " image/svg+xml " public static arrow_left_smooth_img = Img.AsImageElement(Svg.arrow_left_smooth) + public static arrow_left_smooth_svg() { return new FixedUiElement(Svg.arrow_left_smooth);} public static arrow_left_smooth_ui() { return new FixedUiElement(Svg.arrow_left_smooth_img);} - public static arrow_right_smooth = " image/svg+xml " + public static arrow_right_smooth = " image/svg+xml " public static arrow_right_smooth_img = Img.AsImageElement(Svg.arrow_right_smooth) + public static arrow_right_smooth_svg() { return new FixedUiElement(Svg.arrow_right_smooth);} public static arrow_right_smooth_ui() { return new FixedUiElement(Svg.arrow_right_smooth_img);} public static bug = " " public static bug_img = Img.AsImageElement(Svg.bug) + public static bug_svg() { return new FixedUiElement(Svg.bug);} public static bug_ui() { return new FixedUiElement(Svg.bug_img);} - public static camera_plus = " image/svg+xml " + public static camera_plus = " image/svg+xml " public static camera_plus_img = Img.AsImageElement(Svg.camera_plus) + public static camera_plus_svg() { return new FixedUiElement(Svg.camera_plus);} public static camera_plus_ui() { return new FixedUiElement(Svg.camera_plus_img);} - public static checkmark = "" + public static checkmark = "" public static checkmark_img = Img.AsImageElement(Svg.checkmark) + public static checkmark_svg() { return new FixedUiElement(Svg.checkmark);} public static checkmark_ui() { return new FixedUiElement(Svg.checkmark_img);} - public static close = " image/svg+xml " + public static close = " image/svg+xml " public static close_img = Img.AsImageElement(Svg.close) + public static close_svg() { return new FixedUiElement(Svg.close);} public static close_ui() { return new FixedUiElement(Svg.close_img);} - public static crosshair_blue_center = " image/svg+xml " + public static crosshair_blue_center = " image/svg+xml " public static crosshair_blue_center_img = Img.AsImageElement(Svg.crosshair_blue_center) + public static crosshair_blue_center_svg() { return new FixedUiElement(Svg.crosshair_blue_center);} public static crosshair_blue_center_ui() { return new FixedUiElement(Svg.crosshair_blue_center_img);} - public static crosshair_blue = " image/svg+xml " + public static crosshair_blue = " image/svg+xml " public static crosshair_blue_img = Img.AsImageElement(Svg.crosshair_blue) + public static crosshair_blue_svg() { return new FixedUiElement(Svg.crosshair_blue);} public static crosshair_blue_ui() { return new FixedUiElement(Svg.crosshair_blue_img);} - public static crosshair = " image/svg+xml " + public static crosshair = " image/svg+xml " public static crosshair_img = Img.AsImageElement(Svg.crosshair) + public static crosshair_svg() { return new FixedUiElement(Svg.crosshair);} public static crosshair_ui() { return new FixedUiElement(Svg.crosshair_img);} - public static delete_icon = " image/svg+xml " + public static delete_icon = " image/svg+xml " public static delete_icon_img = Img.AsImageElement(Svg.delete_icon) + public static delete_icon_svg() { return new FixedUiElement(Svg.delete_icon);} public static delete_icon_ui() { return new FixedUiElement(Svg.delete_icon_img);} - public static down = " image/svg+xml " + public static down = " image/svg+xml " public static down_img = Img.AsImageElement(Svg.down) + public static down_svg() { return new FixedUiElement(Svg.down);} public static down_ui() { return new FixedUiElement(Svg.down_img);} - public static envelope = " " + public static envelope = " image/svg+xml " public static envelope_img = Img.AsImageElement(Svg.envelope) + public static envelope_svg() { return new FixedUiElement(Svg.envelope);} public static envelope_ui() { return new FixedUiElement(Svg.envelope_img);} - public static floppy = " " + public static floppy = " " public static floppy_img = Img.AsImageElement(Svg.floppy) + public static floppy_svg() { return new FixedUiElement(Svg.floppy);} public static floppy_ui() { return new FixedUiElement(Svg.floppy_img);} - public static gear = "" + public static gear = " " public static gear_img = Img.AsImageElement(Svg.gear) + public static gear_svg() { return new FixedUiElement(Svg.gear);} public static gear_ui() { return new FixedUiElement(Svg.gear_img);} - public static help = " image/svg+xml " + public static help = " " public static help_img = Img.AsImageElement(Svg.help) + public static help_svg() { return new FixedUiElement(Svg.help);} public static help_ui() { return new FixedUiElement(Svg.help_img);} - public static home = " " + public static home = " " public static home_img = Img.AsImageElement(Svg.home) + public static home_svg() { return new FixedUiElement(Svg.home);} public static home_ui() { return new FixedUiElement(Svg.home_img);} - public static josm_logo = " JOSM Logotype 2019 image/svg+xml JOSM Logotype 2019 2019-08-05 Diamond00744 Public Domain " + public static josm_logo = " JOSM Logotype 2019 image/svg+xml JOSM Logotype 2019 2019-08-05 Diamond00744 Public Domain " public static josm_logo_img = Img.AsImageElement(Svg.josm_logo) + public static josm_logo_svg() { return new FixedUiElement(Svg.josm_logo);} public static josm_logo_ui() { return new FixedUiElement(Svg.josm_logo_img);} - public static layers = " image/svg+xml " + public static layers = " image/svg+xml " public static layers_img = Img.AsImageElement(Svg.layers) + public static layers_svg() { return new FixedUiElement(Svg.layers);} public static layers_ui() { return new FixedUiElement(Svg.layers_img);} - public static layersAdd = " image/svg+xml " + public static layersAdd = " image/svg+xml " public static layersAdd_img = Img.AsImageElement(Svg.layersAdd) + public static layersAdd_svg() { return new FixedUiElement(Svg.layersAdd);} public static layersAdd_ui() { return new FixedUiElement(Svg.layersAdd_img);} - public static logo = " image/svg+xml " + public static logo = " image/svg+xml " public static logo_img = Img.AsImageElement(Svg.logo) + public static logo_svg() { return new FixedUiElement(Svg.logo);} public static logo_ui() { return new FixedUiElement(Svg.logo_img);} - public static logout = " image/svg+xml " + public static logout = " image/svg+xml " public static logout_img = Img.AsImageElement(Svg.logout) + public static logout_svg() { return new FixedUiElement(Svg.logout);} public static logout_ui() { return new FixedUiElement(Svg.logout_img);} public static mapillary = "" public static mapillary_img = Img.AsImageElement(Svg.mapillary) + public static mapillary_svg() { return new FixedUiElement(Svg.mapillary);} public static mapillary_ui() { return new FixedUiElement(Svg.mapillary_img);} public static no_checkmark = " " public static no_checkmark_img = Img.AsImageElement(Svg.no_checkmark) + public static no_checkmark_svg() { return new FixedUiElement(Svg.no_checkmark);} public static no_checkmark_ui() { return new FixedUiElement(Svg.no_checkmark_img);} - public static or = " image/svg+xml " + public static or = " image/svg+xml " public static or_img = Img.AsImageElement(Svg.or) + public static or_svg() { return new FixedUiElement(Svg.or);} public static or_ui() { return new FixedUiElement(Svg.or_img);} public static osm_logo_us = "" public static osm_logo_us_img = Img.AsImageElement(Svg.osm_logo_us) + public static osm_logo_us_svg() { return new FixedUiElement(Svg.osm_logo_us);} public static osm_logo_us_ui() { return new FixedUiElement(Svg.osm_logo_us_img);} - public static osm_logo = " OpenStreetMap logo 2011 image/svg+xml OpenStreetMap logo 2011 Ken Vermette April 2011 OpenStreetMap.org Replacement logo for OpenStreetMap Foundation OSM openstreetmap logo http://wiki.openstreetmap.org/wiki/File:Public-images-osm_logo.svg 010110010011010110010011 010110010011010110010011 " + public static osm_logo = " OpenStreetMap logo 2011 image/svg+xml OpenStreetMap logo 2011 Ken Vermette April 2011 OpenStreetMap.org Replacement logo for OpenStreetMap Foundation OSM openstreetmap logo http://wiki.openstreetmap.org/wiki/File:Public-images-osm_logo.svg 010110010011010110010011 010110010011010110010011 " public static osm_logo_img = Img.AsImageElement(Svg.osm_logo) + public static osm_logo_svg() { return new FixedUiElement(Svg.osm_logo);} public static osm_logo_ui() { return new FixedUiElement(Svg.osm_logo_img);} - public static pencil = " " + public static pencil = " " public static pencil_img = Img.AsImageElement(Svg.pencil) + public static pencil_svg() { return new FixedUiElement(Svg.pencil);} public static pencil_ui() { return new FixedUiElement(Svg.pencil_img);} - public static pop_out = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " + public static pop_out = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " public static pop_out_img = Img.AsImageElement(Svg.pop_out) + public static pop_out_svg() { return new FixedUiElement(Svg.pop_out);} public static pop_out_ui() { return new FixedUiElement(Svg.pop_out_img);} - public static reload = " " + public static reload = " " public static reload_img = Img.AsImageElement(Svg.reload) + public static reload_svg() { return new FixedUiElement(Svg.reload);} public static reload_ui() { return new FixedUiElement(Svg.reload_img);} - public static search = " " + public static search = " " public static search_img = Img.AsImageElement(Svg.search) + public static search_svg() { return new FixedUiElement(Svg.search);} public static search_ui() { return new FixedUiElement(Svg.search_img);} - public static share = " image/svg+xml " + public static share = " image/svg+xml " public static share_img = Img.AsImageElement(Svg.share) + public static share_svg() { return new FixedUiElement(Svg.share);} public static share_ui() { return new FixedUiElement(Svg.share_img);} - public static star = " Created by potrace 1.15, written by Peter Selinger 2001-2017 " + public static star = " Created by potrace 1.15, written by Peter Selinger 2001-2017 " public static star_img = Img.AsImageElement(Svg.star) + public static star_svg() { return new FixedUiElement(Svg.star);} public static star_ui() { return new FixedUiElement(Svg.star_img);} - public static statistics = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " + public static statistics = " Svg Vector Icons : http://www.onlinewebfonts.com/icon " public static statistics_img = Img.AsImageElement(Svg.statistics) + public static statistics_svg() { return new FixedUiElement(Svg.statistics);} public static statistics_ui() { return new FixedUiElement(Svg.statistics_img);} - public static up = " " + public static up = " " public static up_img = Img.AsImageElement(Svg.up) + public static up_svg() { return new FixedUiElement(Svg.up);} public static up_ui() { return new FixedUiElement(Svg.up_img);} - public static wikimedia_commons_white = " Wikimedia Commons Logo " + public static wikimedia_commons_white = " Wikimedia Commons Logo " public static wikimedia_commons_white_img = Img.AsImageElement(Svg.wikimedia_commons_white) + public static wikimedia_commons_white_svg() { return new FixedUiElement(Svg.wikimedia_commons_white);} public static wikimedia_commons_white_ui() { return new FixedUiElement(Svg.wikimedia_commons_white_img);} - public static wikipedia = " Wikipedia logo version 2" + public static wikipedia = " Wikipedia logo version 2" public static wikipedia_img = Img.AsImageElement(Svg.wikipedia) + public static wikipedia_svg() { return new FixedUiElement(Svg.wikipedia);} public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);} } diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index bf12c483f..6fe9948aa 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -27,7 +27,8 @@ export class FullScreenMessageBox extends UIElement { "overflow-y: auto;" + "max-width:100vw;" + "overflow-x:hidden;" + - "background:white;" + "background:var(--background-color);" + + "color: var(--foreground-color);" ); }); diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 2fb61bc8d..68367713e 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -44,7 +44,7 @@ export class ImageUploadFlow extends UIElement { this._licensePicker = licensePicker; this._selectedLicence = licensePicker.GetValue(); - this._connectButton = new Combine([t.pleaseLogin]) + this._connectButton = t.pleaseLogin.Clone() .onClick(() => State.state.osmConnection.AttemptLogin()) .SetClass("login-button-friendly"); @@ -101,7 +101,7 @@ export class ImageUploadFlow extends UIElement { ]); const label = new Combine([ - Svg.camera_plus_ui().SetStyle("width: 36px;height: 36px;padding: 0.1em;margin-top: 5px;border-radius: 0;float: left;display:block"), + Svg.camera_plus_svg().SetStyle("width: 36px;height: 36px;padding: 0.1em;margin-top: 5px;border-radius: 0;float: left;display:block"), Translations.t.image.addPicture .SetStyle("width:max-content;font-size: 28px;" + "font-weight: bold;" + @@ -115,7 +115,7 @@ export class ImageUploadFlow extends UIElement { "cursor:pointer;" + "padding: 0.5em;" + "border-radius: 1em;" + - "border: 3px solid black;" + + "border: 3px solid var(--popup-border);" + "box-sizing:border-box;") const actualInputElement = diff --git a/UI/Input/Direction.ts b/UI/Input/Direction.ts new file mode 100644 index 000000000..d7dc5dc10 --- /dev/null +++ b/UI/Input/Direction.ts @@ -0,0 +1,39 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Combine from "../Base/Combine"; +import {FixedUiElement} from "../Base/FixedUiElement"; + +/** + * Selects a direction in degrees + */ +export default class Direction extends InputElement{ + + private readonly value: UIEventSource; + public readonly IsSelected: UIEventSource = new UIEventSource(false); + + constructor(value?: UIEventSource) { + super(); + this.value = value ?? new UIEventSource(undefined); + } + + + GetValue(): UIEventSource { + return this.value; + } + + InnerRender(): string { + return new Combine([ + new FixedUiElement("").SetStyle( + "position: absolute;top: calc(50% - 0.5em);left: calc(50% - 0.5em);width: 1em;height: 1em;background: red;border-radius: 1em"), + + ]) + .SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") + .Render(); + } + + + IsValid(t: number): boolean { + return t >= 0 && t <= 360; + } + +} \ No newline at end of file diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 931b570b6..fce78e676 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -38,7 +38,7 @@ export default class EditableTagRendering extends UIElement { if(State.state.featureSwitchUserbadge.data){ this._editButton = - Svg.pencil_ui().SetClass("edit-button") + Svg.pencil_svg().SetClass("edit-button") .onClick(() => { self._editMode.setData(true); }); diff --git a/UI/UserBadge.ts b/UI/UserBadge.ts index 49e8acfa3..6a84fb352 100644 --- a/UI/UserBadge.ts +++ b/UI/UserBadge.ts @@ -34,7 +34,7 @@ export class UserBadge extends UIElement { .SetClass("userbadge-login") .onClick(() => State.state.osmConnection.AttemptLogin()); this._logout = - Svg.logout_ui() + Svg.logout_svg() .onClick(() => { State.state.osmConnection.LogOut(); }); @@ -52,7 +52,7 @@ export class UserBadge extends UIElement { this._homeButton = new VariableUiElement( this._userDetails.map((userinfo) => { if (userinfo.home) { - return Svg.home_img; + return Svg.home; } return ""; }) @@ -75,7 +75,7 @@ export class UserBadge extends UIElement { let messageSpan: UIElement = new Link( - new Combine([Svg.envelope_img, "" + user.totalMessages]), + new Combine([Svg.envelope, "" + user.totalMessages]), 'https://www.openstreetmap.org/messages/inbox', true ) @@ -83,7 +83,7 @@ export class UserBadge extends UIElement { if (user.unreadMessages > 0) { messageSpan = new Link( - new Combine([Svg.envelope_img, "" + user.unreadMessages]), + new Combine([Svg.envelope, "" + user.unreadMessages]), 'https://www.openstreetmap.org/messages/inbox', true ).SetClass("alert") @@ -104,7 +104,7 @@ export class UserBadge extends UIElement { } const settings = - new Link(Svg.gear_ui(), + new Link(Svg.gear_svg(), `https://www.openstreetmap.org/user/${encodeURIComponent(user.name)}/account`, true) @@ -124,7 +124,7 @@ export class UserBadge extends UIElement { const csCount = new Link( - new Combine([Svg.star_img, "" + user.csCount]), + new Combine([Svg.star, "" + user.csCount]), `https://www.openstreetmap.org/user/${user.name}/history`, true); diff --git a/assets/svg/ampersand.svg b/assets/svg/ampersand.svg index 525a1ef74..f2df861c4 100644 --- a/assets/svg/ampersand.svg +++ b/assets/svg/ampersand.svg @@ -1,4 +1,4 @@ - +e \ No newline at end of file + \ No newline at end of file diff --git a/assets/svg/envelope.svg b/assets/svg/envelope.svg index 8728151cb..74923d307 100644 --- a/assets/svg/envelope.svg +++ b/assets/svg/envelope.svg @@ -1,4 +1,47 @@ - - - - \ No newline at end of file + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/assets/svg/gear.svg b/assets/svg/gear.svg index 302fac4bd..6142f34e9 100644 --- a/assets/svg/gear.svg +++ b/assets/svg/gear.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/assets/svg/help.svg b/assets/svg/help.svg index 1f6bad77d..b6bd68132 100644 --- a/assets/svg/help.svg +++ b/assets/svg/help.svg @@ -8,33 +8,21 @@ id="svg11382" height="900" width="900" + viewBox="0 0 900 900" version="1.0"> - - - - image/svg+xml - - - - - - + transform="matrix(0.90103258,0,0,0.90103258,112.84058,-1.9060177)" + > diff --git a/assets/svg/home.svg b/assets/svg/home.svg index b9f31cf2f..c27eb53a9 100644 --- a/assets/svg/home.svg +++ b/assets/svg/home.svg @@ -1,3 +1,6 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/assets/svg/pencil.svg b/assets/svg/pencil.svg index 6f9610f04..d41508fab 100644 --- a/assets/svg/pencil.svg +++ b/assets/svg/pencil.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/assets/svg/share.svg b/assets/svg/share.svg index d70d17511..83f599f43 100644 --- a/assets/svg/share.svg +++ b/assets/svg/share.svg @@ -73,25 +73,25 @@ id="layer1" transform="translate(0,-270.54165)"> ` + } } const og = ` diff --git a/css/mobile.css b/css/mobile.css index f4c5839fb..6ee07088c 100644 --- a/css/mobile.css +++ b/css/mobile.css @@ -4,6 +4,8 @@ Contains tweaks for small screens .only-on-mobile { display: none; + background-color: var(--background-color); + color: var(--foreground-color); } @media only screen and (max-width: 600px), only screen and (max-height: 600px) { @@ -14,6 +16,8 @@ Contains tweaks for small screens .only-on-mobile { display: unset; + background-color: var(--background-color); + color: var(--foreground-color); } .hidden-on-mobile { @@ -22,6 +26,8 @@ Contains tweaks for small screens #messagesbox { display: none; + background-color: var(--background-color); + color: var(--foreground-color); } #help-button-mobile{ @@ -57,15 +63,15 @@ Contains tweaks for small screens #messagesboxmobile { display: block; + position: absolute; z-index: 10000; - background-color: white; width: 100vw; } #welcomeMessage { display: inline-block; - background-color: white; + background-color: var(--background-color); border-radius: 0; width: 100%; max-width: 100%; diff --git a/css/openinghourstable.css b/css/openinghourstable.css index 5034cff7d..4d04de090 100644 --- a/css/openinghourstable.css +++ b/css/openinghourstable.css @@ -5,7 +5,6 @@ text-align: center; word-break: normal; } - .oh-table th { padding: 0; margin: 0; @@ -34,22 +33,22 @@ } .oh-timecell:hover { - background-color: #92b1ff !important; + background-color: var(--catch-detail-color) !important; } .oh-timecell-selected { - background-color: #0048ff; + background-color: var(--catch-detail-color); } .oh-timecell-half { border-bottom: 1px solid #ddd; - background-color: aliceblue; + background-color: var(--subtle-detail-color); } .oh-timecell-half.oh-timecell-selected { - background-color: #0048ff; + background-color: var(--catch-detail-color); } .oh-table tr { @@ -75,12 +74,12 @@ } .oh-timecol-selected { - border-right: #0048ff; + border-right: var(--catch-detail-color); } .oh-timecol-selected > span { - background-color: #0048ff; - color: white; + background-color: var(--catch-detail-color); + color: var(--background-color); width: 100%; display: block; } @@ -96,11 +95,11 @@ } .oh-timerow-selected .oh-timecell-0 { - border-left: 10px solid #0048ff !important; + border-left: 10px solid var(--catch-detail-color) !important; } .oh-timerow-selected .oh-timecell-6 { - border-right: 10px solid #0048ff !important; + border-right: 10px solid var(--catch-detail-color) !important; } @@ -117,7 +116,7 @@ top: 0; left: 0; width: calc(100% - 4px); - background: #0048ff; + background: var(--catch-detail-color); z-index: 1; box-sizing: border-box; } @@ -276,7 +275,7 @@ .ohviz-today { - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); } .ohviz-weekday { @@ -290,7 +289,7 @@ } .ohviz-container { - border: 0.5em solid #e5f5ff; + border: 0.5em solid var(--subtle-detail-color); border-radius: 1em; display: block; } diff --git a/css/tabbedComponent.css b/css/tabbedComponent.css index 741c7ba36..d895fc9ae 100644 --- a/css/tabbedComponent.css +++ b/css/tabbedComponent.css @@ -7,7 +7,7 @@ flex-wrap: nowrap; justify-content: flex-start; align-items: start; - background-color: white; + background-color: var(--background-color); max-width: 100vw; overflow-x: auto; } @@ -21,10 +21,19 @@ margin:auto; } +.tab-single-header svg { + height: 3em; + max-width: 3em; + padding: 0.5em; + display:block; + margin:auto; +} + .tab-content { z-index: 5002; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); position: relative; padding: 1em; display: inline-block; @@ -41,19 +50,36 @@ } .tab-active { - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); z-index: 5001; - box-shadow: 0 0 10px black; - border: 1px solid white; + box-shadow: 0 0 10px var(--shadow-color); + border: 1px solid var(--background-color); min-width: 4em; } +.tab-active svg { + fill: var(--foreground-color); + stroke: var(--foreground-color); +} + .tab-non-active { - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--foreground-color); opacity: 0.5; border-left: 1px solid gray; - border-right:1px solid gray; - border-top: 1px solid gray; + border-right: 1px solid gray; + border-top: 1px solid gray; border-bottom: 1px solid lightgray; min-width: 4em; } + +.tab-non-active svg { + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; +} + +.tab-non-active svg path{ + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; +} diff --git a/css/tagrendering.css b/css/tagrendering.css index 7c08861e8..705202dda 100644 --- a/css/tagrendering.css +++ b/css/tagrendering.css @@ -30,7 +30,7 @@ .question { display: block; margin-top: 1em; - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); padding: 1em; border-radius: 1em; font-size: larger; @@ -72,7 +72,7 @@ input:checked + label .question-option-with-border { .save { display: inline-block; border: solid white 2px; - background-color: #3a3aeb; + background-color: var(--catch-detail-color); color: white; padding: 0.2em 0.6em; font-size: x-large; @@ -90,8 +90,8 @@ input:checked + label .question-option-with-border { .login-button-friendly { display: inline-block; border: solid white 2px; - background-color: #3a3aeb; - color: white; + background-color:var(--catch-detail-color); + color: var(--catch-detail-color-contrast); padding: 0.2em 0.6em; font-size: large; font-weight: bold; @@ -115,7 +115,24 @@ input:checked + label .question-option-with-border { height: 1.3em; padding: 0.5em; border-radius: 0.65em; - border: solid black 1px; + border: solid var(--popup-border) 1px; font-size: medium; float: right; +} + +.edit-button svg { + width: 1.3em; + height: 1.3em; + padding: 0.5em; + border-radius: 0.65em; + border: solid var(--popup-border) 1px; + stroke: var(--popup-border) !important; + fill: var(--popup-border) !important; + font-size: medium; + float: right; +} + +.edit-button svg path{ + stroke: var(--popup-border) !important; + fill: var(--popup-border) !important; } \ No newline at end of file diff --git a/css/userbadge.css b/css/userbadge.css index 81ac71291..49ddab2df 100644 --- a/css/userbadge.css +++ b/css/userbadge.css @@ -1,6 +1,7 @@ #userbadge { display: inline-block; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em; @@ -15,7 +16,7 @@ #userbadge a { text-decoration: none; - color: black; + color: var(--foreground-color); } @@ -42,14 +43,19 @@ margin-bottom: 0.2em; } -.userstats img { +.userstats svg { width: 1em; height: 1em; - fill: black; - border-radius: 0; + border-radius: 0; display: block; } +.userstats img { + width: 1em; + height: 1em; + border-radius: 0; + display: block; +} #profile-pic { float: left; @@ -76,7 +82,8 @@ height: 2.2em; /*SHould equal profile-pic height - padding*/ z-index: 5000; text-align: left; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); background-size: 100%; line-height: 0.75em; @@ -88,7 +95,8 @@ .userbadge-login { font-weight: bold; font-size: large; - background-color: #e5f5ff !important; + background-color: var(--subtle-detail-color) !important; + color: var(--subtle-detail-color-contrast); height:3em; display: inline-block; diff --git a/generateIncludedImages.ts b/generateIncludedImages.ts index 12374032f..73a8251b9 100644 --- a/generateIncludedImages.ts +++ b/generateIncludedImages.ts @@ -14,6 +14,8 @@ function genImages() { } const svg = fs.readFileSync("./assets/svg/" + path, "utf-8") + .replace(/<\?xml.*?>/, "") + .replace(/fill: ?none;/g,"fill: none !important;") // This is such a brittle hack... .replace(/\n/g, " ") .replace(/\r/g, "") .replace(/\\/g, "\\") @@ -22,6 +24,7 @@ function genImages() { .replace(/[ -]/g, "_"); module += ` public static ${name} = "${svg}"\n` module += ` public static ${name}_img = Img.AsImageElement(Svg.${name})\n` + module += ` public static ${name}_svg() { return new FixedUiElement(Svg.${name});}\n` module += ` public static ${name}_ui() { return new FixedUiElement(Svg.${name}_img);}\n\n` } module += "}\n"; diff --git a/index.css b/index.css index 8df3e9aba..e0bd4e2ef 100644 --- a/index.css +++ b/index.css @@ -1,11 +1,33 @@ +:root { + --subtle-detail-color: #e5f5ff; + --subtle-detail-color-contrast: black; + --catch-detail-color: #3a3aeb; + --catch-detail-color-contrast: white; + --alert-color: #fee4d1; + --background-color: white; + --foreground-color: black; + --popup-border: white; + --shadow-color: #00000066 +} + html, body { height: 100%; margin: 0; padding: 0; + background-color: var(--background-color); + color: var(--foreground-color); + font-family: 'Helvetica Neue', Arial, sans-serif; } -body { - font-family: 'Helvetica Neue', Arial, sans-serif; + +svg { + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; +} + +svg path { + fill: var(--foreground-color) !important; + stroke: var(--foreground-color) !important; } @@ -13,6 +35,17 @@ body { height: 100%; } +.leaflet-popup-content-wrapper { + background-color: var(--background-color); + color: var(--foreground-color); + + border: 2px solid var(--popup-border) +} + +.leaflet-container { + background-color: var(--background-color) !important; +} + #geolocate-button { position: absolute; bottom: 25px; @@ -39,22 +72,25 @@ body { bottom: 1em; left: 1em; z-index: 9000; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + border-radius: 1em; cursor: pointer; - box-shadow: 0 0 10px #00000066; + box-shadow: 0 0 10px var(--shadow-color); } .layer-selection-toggle { - border-top-left-radius: 1em; - border-bottom-left-radius: 1em; + border-radius: 1em; display: flex; flex-direction: column-reverse; + background: var(--subtle-detail-color); } -.layer-selection-toggle img { +.layer-selection-toggle svg { display: block; width: 2em; + height: 2em; padding: 1em; } @@ -62,7 +98,7 @@ body { .alert { - background-color: #fee4d1; + background-color: var(--alert-color); font-weight: bold; border-radius: 1em; margin: 0.25em; @@ -82,7 +118,7 @@ body { } .shadow { - box-shadow: 0 0 10px #00000066; + box-shadow: 0 0 10px var(--shadow-color); } .title-font span { @@ -91,7 +127,8 @@ body { } .soft { - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); font-weight: bold; border-radius: 1em; margin: 0.25em; @@ -137,7 +174,9 @@ body { #searchbox { display: inline-block; text-align: left; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + transition: all 500ms linear; pointer-events: all; border-radius: 1.3em; @@ -170,6 +209,7 @@ body { font-size: large; width: 100%; box-sizing: border-box; + color: var(--foreground-color); } .search-go img { @@ -215,13 +255,16 @@ body { overflow-y: auto; border-top-right-radius: 1em; border-bottom-right-radius: 1em; + background-color: var(--background-color); + color: var(--foreground-color); } .close-welcome-button { position: absolute; display: inline-block; height: 100%; - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); box-sizing: border-box; width: 4em; padding: 1em; @@ -229,22 +272,26 @@ body { border-bottom-left-radius: 1em; } -.close-welcome-button img { +.close-welcome-button svg { width: 2em; + height: 2em; } .open-welcome-button { display: inline-block; box-sizing: border-box; - background-color: white; + background: var(--subtle-detail-color); + color: var(--foreground-color); + height: 4em; width: 4em; padding: 1em; border-radius: 1em; } -.open-welcome-button img { +.open-welcome-button svg { width: 2em; + height: 2em; } #messagesbox { @@ -252,9 +299,11 @@ body { position: relative; padding: 0; pointer-events: all; - box-shadow: 0 0 10px #00000066; + box-shadow: 0 0 10px var(--shadow-color); border-radius: 1em; - width: min-content + width: min-content; + background-color: var(--background-color); + color: var(--foreground-color); } @@ -273,7 +322,9 @@ body { pointer-events: none; opacity: 1; - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + transition: opacity 500ms linear; @@ -357,7 +408,9 @@ body { .iframe-escape { - background-color: white; + background-color: var(--background-color); + color: var(--foreground-color); + border-radius: 2em; display: block; width: min-content; @@ -376,11 +429,11 @@ body { flex-direction: row; font-size: large; margin: 0.5em; - background-color: #e5f5ff; + background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); border-radius: 1em; align-items: center; text-decoration: none; - color: black; } diff --git a/test.ts b/test.ts index 77feaa558..f1b338122 100644 --- a/test.ts +++ b/test.ts @@ -1,20 +1,11 @@ -/* +//* -import {UIEventSource} from "./Logic/UIEventSource"; -import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox"; -import SharedLayers from "./Customizations/SharedLayers"; +import Direction from "./UI/Input/Direction"; -const tags = { - mapillary: "wweALGY5g8_T8UjGkcWCfw", - wikimedia_commons: "File:Boekenkast Sint-Lodewijks.jpg" -} -const src = new UIEventSource(tags); +new Direction().AttachTo("maindiv") -new FeatureInfoBox(src, SharedLayers.sharedLayers["ghost_bike"]).AttachTo('maindiv'); -//const subs = new SubstitutedTranslation(new Translation({"nl":"NL {image_carousel()} {image_upload()}"}), src) -//subs.AttachTo("maindiv") /*/ From c86f4e4aff99fccd62d430e19b5cc83289fc8469 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 14 Nov 2020 03:26:09 +0100 Subject: [PATCH 04/20] More css tweaks --- InitUiElements.ts | 9 +-------- State.ts | 2 +- Svg.ts | 2 +- Utils.ts | 11 +++++++++++ assets/svg/checkmark.svg | 2 +- .../surveillance_cameras/custom_theme.css | 6 +++--- .../surveillance_cameras.json | 2 +- css/tagrendering.css | 19 ++++++++++--------- index.css | 3 ++- index.ts | 5 +++++ 10 files changed, 36 insertions(+), 25 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index faa8dbbf3..286d2949a 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -111,14 +111,7 @@ export class InitUiElements { InitUiElements.setupAllLayerElements(); if (layoutToUse.customCss !== undefined) { - var head = document.getElementsByTagName('head')[0]; - var link = document.createElement('link'); - link.id = "customCss"; - link.rel = 'stylesheet'; - link.type = 'text/css'; - link.href = layoutToUse.customCss; - link.media = 'all'; - head.appendChild(link); + Utils.LoadCustomCss(layoutToUse.customCss); } function updateFavs() { diff --git a/State.ts b/State.ts index 5d7b63fba..f3da37633 100644 --- a/State.ts +++ b/State.ts @@ -22,7 +22,7 @@ export default class State { // The singleton of the global state public static state: State; - public static vNumber = "0.1.2f"; + public static vNumber = "0.1.3"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Svg.ts b/Svg.ts index a3a3de197..1053b4836 100644 --- a/Svg.ts +++ b/Svg.ts @@ -39,7 +39,7 @@ export default class Svg { public static camera_plus_svg() { return new FixedUiElement(Svg.camera_plus);} public static camera_plus_ui() { return new FixedUiElement(Svg.camera_plus_img);} - public static checkmark = "" + public static checkmark = "" public static checkmark_img = Img.AsImageElement(Svg.checkmark) public static checkmark_svg() { return new FixedUiElement(Svg.checkmark);} public static checkmark_ui() { return new FixedUiElement(Svg.checkmark_img);} diff --git a/Utils.ts b/Utils.ts index 9e5cdf761..e6c108426 100644 --- a/Utils.ts +++ b/Utils.ts @@ -154,5 +154,16 @@ export class Utils { }); } + + public static LoadCustomCss(location: string){ + var head = document.getElementsByTagName('head')[0]; + var link = document.createElement('link'); + link.id = "customCss"; + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = location; + link.media = 'all'; + head.appendChild(link); + } } diff --git a/assets/svg/checkmark.svg b/assets/svg/checkmark.svg index 811295e27..e206d6370 100644 --- a/assets/svg/checkmark.svg +++ b/assets/svg/checkmark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/themes/surveillance_cameras/custom_theme.css b/assets/themes/surveillance_cameras/custom_theme.css index ec4d17f17..afbb78791 100644 --- a/assets/themes/surveillance_cameras/custom_theme.css +++ b/assets/themes/surveillance_cameras/custom_theme.css @@ -1,12 +1,12 @@ html { - --subtle-detail-color: #9d9d9d !important; - --subtle-detail-color-contrast: #00ff00 !important; + --subtle-detail-color: #070 !important; + --subtle-detail-color-contrast: #white !important; --popup-border: #00ff00 !important; --catch-detail-color: #00ff00 !important; --catch-detail-color-contrast: black !important; --alert-color: #eb00ff !important; --background-color: black !important; --foreground-color: white !important; - --shadow-color: white !important; + --shadow-color: #0f0 !important; } diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index dbf40f75c..7d772603f 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -156,7 +156,7 @@ "nl": "Wat wordt hier precies bewaakt?" }, "freeform": { - "key": "surveillance:type" + "key": "surveillance:zone" }, "render": { "en": " Surveills a {surveillance:zone}", diff --git a/css/tagrendering.css b/css/tagrendering.css index 705202dda..769468743 100644 --- a/css/tagrendering.css +++ b/css/tagrendering.css @@ -31,6 +31,7 @@ display: block; margin-top: 1em; background-color: var(--subtle-detail-color); + color: var(--subtle-detail-color-contrast); padding: 1em; border-radius: 1em; font-size: larger; @@ -65,15 +66,15 @@ } input:checked + label .question-option-with-border { - border: 2px solid black; + border: 2px solid var(--subtle-detail-color-contrast); } .save { display: inline-block; - border: solid white 2px; + border: solid var(--catch-detail-color-contrast) 2px; background-color: var(--catch-detail-color); - color: white; + color: var(--catch-detail-color-contrast); padding: 0.2em 0.6em; font-size: x-large; font-weight: bold; @@ -89,9 +90,9 @@ input:checked + label .question-option-with-border { .login-button-friendly { display: inline-block; - border: solid white 2px; background-color:var(--catch-detail-color); color: var(--catch-detail-color-contrast); + border: solid var(--catch-detail-color-contrast) 2px; padding: 0.2em 0.6em; font-size: large; font-weight: bold; @@ -125,14 +126,14 @@ input:checked + label .question-option-with-border { height: 1.3em; padding: 0.5em; border-radius: 0.65em; - border: solid var(--popup-border) 1px; - stroke: var(--popup-border) !important; - fill: var(--popup-border) !important; + border: solid var(--foreground-color) 1px; + stroke: var(--foreground-color) !important; + fill: var(--foreground-color) !important; font-size: medium; float: right; } .edit-button svg path{ - stroke: var(--popup-border) !important; - fill: var(--popup-border) !important; + stroke: var(--foreground-color) !important; + fill: var(--foreground-color) !important; } \ No newline at end of file diff --git a/index.css b/index.css index e0bd4e2ef..54dc23555 100644 --- a/index.css +++ b/index.css @@ -39,7 +39,8 @@ svg path { background-color: var(--background-color); color: var(--foreground-color); - border: 2px solid var(--popup-border) + border: 2px solid var(--popup-border); + box-shadow: 0 3px 14px var(--shadow-color) !important; } .leaflet-container { diff --git a/index.ts b/index.ts index 62b53f833..22098bc1d 100644 --- a/index.ts +++ b/index.ts @@ -5,6 +5,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters"; import {UIEventSource} from "./Logic/UIEventSource"; import * as $ from "jquery"; import LayoutConfig from "./Customizations/JSON/LayoutConfig"; +import {Utils} from "./Utils"; let defaultLayout = "bookcases" // --------------------- Special actions based on the parameters ----------------- @@ -29,6 +30,10 @@ if(location.href.indexOf("pietervdvn.github.io") >= 0){ defaultLayout = "bookcases" } +const customCssQP = QueryParameters.GetQueryParameter("custom-css", "", "If specified, the custom css from the given link will be loaded additionaly"); +if(customCssQP.data !== undefined && customCssQP.data !== ""){ + Utils.LoadCustomCss(customCssQP.data); +} let testing: UIEventSource; From 9978879536872c45da193abe8217d2b5dfb9f128 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 00:27:55 +0100 Subject: [PATCH 05/20] Further theme tweaks, attempts to optimize --- InitUiElements.ts | 2 +- Logic/FilteredLayer.ts | 19 +++--- UI/FullScreenMessageBoxHandler.ts | 60 +++++++++---------- UI/Popup/FeatureInfoBox.ts | 1 + Utils.ts | 1 + .../surveillance_cameras/custom_theme.css | 4 +- .../surveillance_cameras.json | 9 ++- index.css | 18 ++++-- 8 files changed, 62 insertions(+), 52 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index 286d2949a..d6ba7b9e7 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -475,7 +475,7 @@ export class InitUiElements { throw "Layer " + layer + " was not substituted"; } - const generateInfo = (tagsES, feature) => { + const generateInfo = (tagsES) => { return new FeatureInfoBox( tagsES, diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index bcf71c4f2..c90e441dc 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -123,13 +123,9 @@ export class FilteredLayer { } }) } - - static fromDefinition( - definition, - showOnPopup: (tags: UIEventSource, feature: any) => UIElement): - FilteredLayer { - return new FilteredLayer( - definition, showOnPopup); + + static fromDefinition(definition, showOnPopup: (tags: UIEventSource, feature: any) => UIElement): FilteredLayer { + return new FilteredLayer(definition, showOnPopup); } @@ -239,7 +235,7 @@ export class FilteredLayer { } - // The data is split in two parts: the poinst and the rest + // The data is split in two parts: the point and the rest // The points get a special treatment in order to render them properly // Note that some features might get a point representation as well @@ -326,20 +322,23 @@ export class FilteredLayer { eventSource.addCallback(updateStyle); function openPopup(e) { - State.state.selectedElement.setData({feature: feature}); updateStyle() + + if (feature.geometry.type === "Point") { + State.state.selectedElement.setData({feature: feature}); return; // Points bind there own popups } const uiElement = self._showOnPopup(eventSource, feature); - L.popup({ autoPan: true, }).setContent(uiElement.Render()) .setLatLng(e.latlng) .openOn(State.state.bm.map); uiElement.Update(); + State.state.selectedElement.setData({feature: feature}); + if (e) { L.DomEvent.stop(e); // Marks the event as consumed } diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index 6fe9948aa..fe757cac8 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -10,41 +10,19 @@ export class FullScreenMessageBox extends UIElement { private static readonly _toTheMap_height : string = "5em"; - private _uielement: UIElement; private readonly returnToTheMap: UIElement; constructor(onClear: (() => void)) { super(State.state.fullScreenMessage); - const self = this; - State.state.fullScreenMessage.addCallbackAndRun(uiElement => { - this._uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( - "display:block;"+ - "padding: 1em;"+ - "padding-bottom:6em;"+ - `margin-bottom:${FullScreenMessageBox._toTheMap_height};`+ - "box-sizing:border-box;"+ - `height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});`+ - "overflow-y: auto;" + - "max-width:100vw;" + - "overflow-x:hidden;" + - "background:var(--background-color);" + - "color: var(--foreground-color);" - - ); - }); - - this.HideOnEmpty(true); - - State.state.fullScreenMessage.addCallback(latestData => { - if (latestData === undefined) { + State.state.fullScreenMessage.addCallbackAndRun(uiElement => { + if (uiElement === undefined) { location.hash = ""; } else { // The 'hash' makes sure a new piece of history is added. This makes the 'back-button' on android remove the popup location.hash = "#element"; } - this.Update(); - }) + }); if (window !== undefined) { window.onhashchange = function () { @@ -57,14 +35,15 @@ export class FullScreenMessageBox extends UIElement { } } + const self = this; this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) - .SetStyle("background:#7ebc6f;" + - "position: fixed;" + - "z-index: 10000;" + - "bottom: 0;" + - "left: 0;" + - `height: ${FullScreenMessageBox._toTheMap_height};` + + .SetStyle("background:#7ebc6f;" + + "position: fixed;" + + "z-index: 10000;" + + "bottom: 0;" + + "left: 0;" + + `height: ${FullScreenMessageBox._toTheMap_height};` + "width: 100vw;" + "color: white;" + "font-weight: bold;" + @@ -88,7 +67,24 @@ export class FullScreenMessageBox extends UIElement { if (State.state.fullScreenMessage.data === undefined) { return ""; } - return new Combine([this._uielement, this.returnToTheMap]) + + const el = document.getElementById(this.id); + console.warn(el, el.style.display); + + const uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( + "display:block;" + + "padding: 1em;" + + "padding-bottom:6em;" + + `margin-bottom:${FullScreenMessageBox._toTheMap_height};` + + "box-sizing:border-box;" + + `height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});` + + "overflow-y: auto;" + + "max-width:100vw;" + + "overflow-x:hidden;" + + "background:var(--background-color);" + + "color: var(--foreground-color);" + ); + return new Combine([uielement, this.returnToTheMap]) .Render(); } diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index ed66eb2a6..c6f59b04c 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -40,6 +40,7 @@ export class FeatureInfoBox extends UIElement { } InnerRender(): string { + console.error("Inner rendering infobox for ", this._tags.data.id, this.id) return new Combine([ new Combine([this._title, this._titleIcons]) .SetClass("featureinfobox-titlebar"), diff --git a/Utils.ts b/Utils.ts index e6c108426..423ddb3e1 100644 --- a/Utils.ts +++ b/Utils.ts @@ -164,6 +164,7 @@ export class Utils { link.href = location; link.media = 'all'; head.appendChild(link); + console.log("Added custom layout ",location) } } diff --git a/assets/themes/surveillance_cameras/custom_theme.css b/assets/themes/surveillance_cameras/custom_theme.css index afbb78791..d68e02a28 100644 --- a/assets/themes/surveillance_cameras/custom_theme.css +++ b/assets/themes/surveillance_cameras/custom_theme.css @@ -1,6 +1,6 @@ html { - --subtle-detail-color: #070 !important; - --subtle-detail-color-contrast: #white !important; + --subtle-detail-color: #2c2 !important; + --subtle-detail-color-contrast: white !important; --popup-border: #00ff00 !important; --catch-detail-color: #00ff00 !important; --catch-detail-color-contrast: black !important; diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 7d772603f..c38e55e93 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -111,7 +111,7 @@ }, { "question": { - "en": "What k ind of surveillance is this camera", + "en": "What kind of surveillance is this camera", "nl": "Wat soort bewaking wordt hier uitgevoerd?" }, "mappings": [ @@ -245,7 +245,12 @@ "color": { "render": "#00f" }, - "presets": [], + "presets": [ + { + "tags": ["man_made=surveillance","surveillance:type=camera"], + "title": "Surveillance camera" + } + ], "wayHandling": 2 } ], diff --git a/index.css b/index.css index 54dc23555..d5c4f874c 100644 --- a/index.css +++ b/index.css @@ -19,6 +19,9 @@ html, body { font-family: 'Helvetica Neue', Arial, sans-serif; } +a { + color: var(--foreground-color) +} svg { fill: var(--foreground-color) !important; @@ -38,7 +41,6 @@ svg path { .leaflet-popup-content-wrapper { background-color: var(--background-color); color: var(--foreground-color); - border: 2px solid var(--popup-border); box-shadow: 0 3px 14px var(--shadow-color) !important; } @@ -47,6 +49,12 @@ svg path { background-color: var(--background-color) !important; } +.leaflet-popup-tip { + background-color: var(--popup-border) !important; + color: var(--popup-border) !important; + box-shadow: 0 3px 14px var(--shadow-color) !important; +} + #geolocate-button { position: absolute; bottom: 25px; @@ -92,7 +100,7 @@ svg path { display: block; width: 2em; height: 2em; - padding: 1em; + padding: 0.75em; } /**************** GENERIC ****************/ @@ -284,9 +292,9 @@ svg path { background: var(--subtle-detail-color); color: var(--foreground-color); - height: 4em; - width: 4em; - padding: 1em; + height: 3.5em; + width: 3.5em; + padding: 0.75em; border-radius: 1em; } From e74b4e380498133c90ebda900b116730daa74f70 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 01:16:35 +0100 Subject: [PATCH 06/20] Small fixes, perf improvements, remove duplicate images of ghost bikes, add wall mount --- InitUiElements.ts | 4 +-- Logic/FilteredLayer.ts | 16 ++++------ State.ts | 2 +- Svg.ts | 2 +- UI/FullScreenMessageBoxHandler.ts | 7 ++-- UI/Image/ImageUploadFlow.ts | 2 +- UI/Popup/FeatureInfoBox.ts | 1 - UI/Popup/TagRenderingAnswer.ts | 5 ++- UI/SimpleAddUI.ts | 2 +- UI/WelcomeMessage.ts | 3 +- assets/layers/ghost_bike/ghost_bike.json | 3 +- assets/svg/camera-plus.svg | 4 +-- .../surveillance_cameras.json | 32 +++++++++++++++++++ 13 files changed, 57 insertions(+), 26 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index d6ba7b9e7..580153fa3 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -153,10 +153,10 @@ export class InitUiElements { * This is given to the div which renders fullscreen on mobile devices */ State.state.selectedElement.addCallback((feature) => { - if (feature?.feature?.properties === undefined) { + if (feature?.properties === undefined) { return; } - const data = feature.feature.properties; + const data = feature.properties; // Which is the applicable set? for (const layer of layoutToUse.layers) { if (typeof layer === "string") { diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index c90e441dc..5e0a106c5 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -322,14 +322,9 @@ export class FilteredLayer { eventSource.addCallback(updateStyle); function openPopup(e) { - updateStyle() - - if (feature.geometry.type === "Point") { - State.state.selectedElement.setData({feature: feature}); - return; // Points bind there own popups + return; // Points bind their own popups } - const uiElement = self._showOnPopup(eventSource, feature); L.popup({ autoPan: true, @@ -337,14 +332,17 @@ export class FilteredLayer { .setLatLng(e.latlng) .openOn(State.state.bm.map); uiElement.Update(); - State.state.selectedElement.setData({feature: feature}); - if (e) { L.DomEvent.stop(e); // Marks the event as consumed } } - layer.on("click", openPopup); + layer.on("click", (e) => { + updateStyle(); + openPopup(e); + State.state.selectedElement.setData(feature); + + }); } }); diff --git a/State.ts b/State.ts index f3da37633..89aa91537 100644 --- a/State.ts +++ b/State.ts @@ -82,7 +82,7 @@ export default class State { /** The latest element that was selected - used to generate the right UI at the right place */ - public readonly selectedElement = new UIEventSource<{ feature: any }>(undefined); + public readonly selectedElement = new UIEventSource(undefined); public readonly zoom: UIEventSource; public readonly lat: UIEventSource; diff --git a/Svg.ts b/Svg.ts index 1053b4836..3041d22cc 100644 --- a/Svg.ts +++ b/Svg.ts @@ -34,7 +34,7 @@ export default class Svg { public static bug_svg() { return new FixedUiElement(Svg.bug);} public static bug_ui() { return new FixedUiElement(Svg.bug_img);} - public static camera_plus = " image/svg+xml " + public static camera_plus = " image/svg+xml " public static camera_plus_img = Img.AsImageElement(Svg.camera_plus) public static camera_plus_svg() { return new FixedUiElement(Svg.camera_plus);} public static camera_plus_ui() { return new FixedUiElement(Svg.camera_plus_img);} diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index fe757cac8..c6b7a1f1c 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -13,9 +13,11 @@ export class FullScreenMessageBox extends UIElement { private readonly returnToTheMap: UIElement; constructor(onClear: (() => void)) { - super(State.state.fullScreenMessage); + super(); this.HideOnEmpty(true); + const self = this; State.state.fullScreenMessage.addCallbackAndRun(uiElement => { + self.Update(); if (uiElement === undefined) { location.hash = ""; } else { @@ -28,14 +30,12 @@ export class FullScreenMessageBox extends UIElement { window.onhashchange = function () { if (location.hash === "") { // No more element: back to the map! - console.log("Clearing full screen message"); State.state.fullScreenMessage.setData(undefined); onClear(); } } } - const self = this; this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) .SetStyle("background:#7ebc6f;" + @@ -69,7 +69,6 @@ export class FullScreenMessageBox extends UIElement { } const el = document.getElementById(this.id); - console.warn(el, el.style.display); const uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( "display:block;" + diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 68367713e..aa75f7bbf 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -115,7 +115,7 @@ export class ImageUploadFlow extends UIElement { "cursor:pointer;" + "padding: 0.5em;" + "border-radius: 1em;" + - "border: 3px solid var(--popup-border);" + + "border: 3px solid var(--foreground-color);" + "box-sizing:border-box;") const actualInputElement = diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index c6f59b04c..ed66eb2a6 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -40,7 +40,6 @@ export class FeatureInfoBox extends UIElement { } InnerRender(): string { - console.error("Inner rendering infobox for ", this._tags.data.id, this.id) return new Combine([ new Combine([this._title, this._titleIcons]) .SetClass("featureinfobox-titlebar"), diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index f560bf293..9bafc24ec 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -9,6 +9,7 @@ import {SubstitutedTranslation} from "../SpecialVisualizations"; export default class TagRenderingAnswer extends UIElement { private _tags: UIEventSource; private _configuration: TagRenderingConfig; + private _content: UIElement; constructor(tags: UIEventSource, configuration: TagRenderingConfig) { super(tags); @@ -31,7 +32,9 @@ export default class TagRenderingAnswer extends UIElement { if (tr === undefined) { return ""; } - return new SubstitutedTranslation(tr, this._tags).Render(); + // Bit of a hack; remember that the fields are updated + this._content = new SubstitutedTranslation(tr, this._tags); + return this._content.Render(); } } \ No newline at end of file diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index bed317914..c4acc5f46 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -116,7 +116,7 @@ export class SimpleAddUI extends UIElement { const loc = State.state.bm.LastClickLocation.data; let feature = State.state.changes.createElement(tags, loc.lat, loc.lon); layerToAddTo.AddNewElement(feature); - State.state.selectedElement.setData({feature: feature}); + State.state.selectedElement.setData(feature); } } diff --git a/UI/WelcomeMessage.ts b/UI/WelcomeMessage.ts index 8fe2c0086..4179285f5 100644 --- a/UI/WelcomeMessage.ts +++ b/UI/WelcomeMessage.ts @@ -24,9 +24,10 @@ export class WelcomeMessage extends UIElement { this.description = new Combine([ "

", layout.title, "

", layout.description - ]) layout.descriptionTail + + this.plzLogIn = diff --git a/assets/layers/ghost_bike/ghost_bike.json b/assets/layers/ghost_bike/ghost_bike.json index fce699803..d33d7df02 100644 --- a/assets/layers/ghost_bike/ghost_bike.json +++ b/assets/layers/ghost_bike/ghost_bike.json @@ -43,8 +43,7 @@ } ], "tagRenderings": [ - "images", - { + { "render": { "en": "A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.", "nl": "Een Witte Fiets (of Spookfiets) is een aandenken aan een fietser die bij een verkeersongeval om het leven kwam. Het gaat over een witgeschilderde fiets die geplaatst werd in de buurt van het ongeval.", diff --git a/assets/svg/camera-plus.svg b/assets/svg/camera-plus.svg index 77201f22d..32f8c1317 100644 --- a/assets/svg/camera-plus.svg +++ b/assets/svg/camera-plus.svg @@ -65,12 +65,12 @@ diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index c38e55e93..994a827f1 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -230,6 +230,38 @@ } } ] + }, + { + "question": { + "en": "How is this camera placed?", + "nl": "Hoe is deze camera geplaatst?" + }, + "freeform": { + "key": "camera:mount" + }, + "mappings": [ + { + "if": "camera:mount=wall", + "then": { + "en": "This camera is placed against a wall", + "nl": "Deze camera hangt aan een muur" + } + }, + { + "if": "camera:mount=pole", + "then": { + "en": "This camera is placed one a pole", + "nl": "Deze camera staat op een paal" + } + }, + { + "if": "camera:mount=pole", + "then": { + "en": "This camera is placed one a pole", + "nl": "Deze camera staat op een paal" + } + } + ] } ], "hideUnderlayingFeaturesMinPercentage": 0, From 67bd817a38c7a5ac636b5ee1883bbd3ae719d927 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 01:20:30 +0100 Subject: [PATCH 07/20] Css tweaks --- InitUiElements.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index 580153fa3..d97819788 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -368,7 +368,7 @@ export class InitUiElements { return; } - layerControlPanel.SetStyle("display:block;padding:1em;border-radius:1em;"); + layerControlPanel.SetStyle("display:block;padding:0.75em;border-radius:1em;"); const closeButton = Svg.close_svg().SetClass("layer-selection-toggle").SetStyle(" background: var(--subtle-detail-color);") const checkbox = new CheckBox( new Combine([ From 7ef2f429f2be5e54451bbc83976c467797af86c0 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 03:10:44 +0100 Subject: [PATCH 08/20] Add fancy direction picker --- Svg.ts | 10 + UI/Input/Direction.ts | 39 ---- UI/Input/DirectionInput.ts | 92 ++++++++ UI/Input/ValidatedTextField.ts | 12 ++ UI/ShareScreen.ts | 24 ++- assets/svg/compass.svg | 199 ++++++++++++++++++ assets/svg/direction.svg | 31 +++ .../surveillance_cameras.json | 11 + index.css | 6 + package-lock.json | 6 +- test.ts | 9 +- 11 files changed, 386 insertions(+), 53 deletions(-) delete mode 100644 UI/Input/Direction.ts create mode 100644 UI/Input/DirectionInput.ts create mode 100644 assets/svg/compass.svg create mode 100644 assets/svg/direction.svg diff --git a/Svg.ts b/Svg.ts index 3041d22cc..4850a0c71 100644 --- a/Svg.ts +++ b/Svg.ts @@ -49,6 +49,11 @@ export default class Svg { public static close_svg() { return new FixedUiElement(Svg.close);} public static close_ui() { return new FixedUiElement(Svg.close_img);} + public static compass = " image/svg+xml N S E W NW SW NE SE " + public static compass_img = Img.AsImageElement(Svg.compass) + public static compass_svg() { return new FixedUiElement(Svg.compass);} + public static compass_ui() { return new FixedUiElement(Svg.compass_img);} + public static crosshair_blue_center = " image/svg+xml " public static crosshair_blue_center_img = Img.AsImageElement(Svg.crosshair_blue_center) public static crosshair_blue_center_svg() { return new FixedUiElement(Svg.crosshair_blue_center);} @@ -69,6 +74,11 @@ export default class Svg { public static delete_icon_svg() { return new FixedUiElement(Svg.delete_icon);} public static delete_icon_ui() { return new FixedUiElement(Svg.delete_icon_img);} + public static direction = " image/svg+xml " + public static direction_img = Img.AsImageElement(Svg.direction) + public static direction_svg() { return new FixedUiElement(Svg.direction);} + public static direction_ui() { return new FixedUiElement(Svg.direction_img);} + public static down = " image/svg+xml " public static down_img = Img.AsImageElement(Svg.down) public static down_svg() { return new FixedUiElement(Svg.down);} diff --git a/UI/Input/Direction.ts b/UI/Input/Direction.ts deleted file mode 100644 index d7dc5dc10..000000000 --- a/UI/Input/Direction.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {InputElement} from "./InputElement"; -import {UIEventSource} from "../../Logic/UIEventSource"; -import Combine from "../Base/Combine"; -import {FixedUiElement} from "../Base/FixedUiElement"; - -/** - * Selects a direction in degrees - */ -export default class Direction extends InputElement{ - - private readonly value: UIEventSource; - public readonly IsSelected: UIEventSource = new UIEventSource(false); - - constructor(value?: UIEventSource) { - super(); - this.value = value ?? new UIEventSource(undefined); - } - - - GetValue(): UIEventSource { - return this.value; - } - - InnerRender(): string { - return new Combine([ - new FixedUiElement("").SetStyle( - "position: absolute;top: calc(50% - 0.5em);left: calc(50% - 0.5em);width: 1em;height: 1em;background: red;border-radius: 1em"), - - ]) - .SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") - .Render(); - } - - - IsValid(t: number): boolean { - return t >= 0 && t <= 360; - } - -} \ No newline at end of file diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts new file mode 100644 index 000000000..7c6f15228 --- /dev/null +++ b/UI/Input/DirectionInput.ts @@ -0,0 +1,92 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import Combine from "../Base/Combine"; +import Svg from "../../Svg"; + +/** + * Selects a direction in degrees + */ +export default class DirectionInput extends InputElement { + + private readonly value: UIEventSource; + public readonly IsSelected: UIEventSource = new UIEventSource(false); + + constructor(value?: UIEventSource) { + super(); + this.dumbMode = false; + this.value = value ?? new UIEventSource(undefined); + + this.value.addCallbackAndRun(rotation => { + const selfElement = document.getElementById(this.id); + if (selfElement === null) { + return; + } + const cone = selfElement.getElementsByClassName("direction-svg")[0] as HTMLElement + cone.style.rotate = rotation + "deg"; + + }) + + } + + + GetValue(): UIEventSource { + return this.value; + } + + InnerRender(): string { + console.log("Inner render direction") + return new Combine([ + Svg.direction_svg().SetStyle( + `position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotategs:${this.value.data}deg;`) + .SetClass("direction-svg"), + Svg.compass_svg().SetStyle( + "position: absolute;top: 0;left: 0;width: 100%;height: 100%;") + ]) + .SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") + .Render(); + } + + protected InnerUpdate(htmlElement: HTMLElement) { + console.log("Inner update direction") + super.InnerUpdate(htmlElement); + const self = this; + + function onPosChange(x: number, y: number) { + const rect = htmlElement.getBoundingClientRect(); + const dx = -(rect.left + rect.right) / 2 + x; + const dy = (rect.top + rect.bottom) / 2 - y; + const angle = 180 * Math.atan2(dy, dx) / Math.PI; + const angleGeo = Math.floor((450 - angle) % 360); + self.value.setData(""+angleGeo) + } + + + htmlElement.ontouchmove = (ev: TouchEvent) => { + onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); + } + + let isDown = false; + + htmlElement.onmousedown = (ev: MouseEvent) => { + isDown = true; + onPosChange(ev.x, ev.y); + ev.preventDefault(); + } + + htmlElement.onmouseup = (ev) => { + isDown = false; ev.preventDefault(); + } + + htmlElement.onmousemove = (ev: MouseEvent) => { + if (isDown) { + onPosChange(ev.x, ev.y); + } ev.preventDefault(); + } + } + + IsValid(str: string): boolean { + const t = Number(str); + return !isNaN(t) && t >= 0 && t <= 360; + } + +} \ No newline at end of file diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 2c89ef691..9ba1a18a3 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -9,6 +9,7 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import CombinedInputElement from "./CombinedInputElement"; import SimpleDatePicker from "./SimpleDatePicker"; import OpeningHoursInput from "./OpeningHours/OpeningHoursInput"; +import DirectionInput from "./DirectionInput"; interface TextFieldDef { name: string, @@ -97,6 +98,17 @@ export default class ValidatedTextField { str = "" + str; return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 }), + ValidatedTextField.tp( + "direction", + "A geographical direction, in degrees. 0° is north, 90° is east", + (str) => { + str = "" + str; + return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 && Number(str) <= 360 + },str => str, + (value) => { + return new DirectionInput(value); + } + ), ValidatedTextField.tp( "float", "A decimal", diff --git a/UI/ShareScreen.ts b/UI/ShareScreen.ts index a682c6c51..c9ea0263b 100644 --- a/UI/ShareScreen.ts +++ b/UI/ShareScreen.ts @@ -32,9 +32,17 @@ export class ShareScreen extends UIElement { const optionCheckboxes: UIElement[] = [] const optionParts: (UIEventSource)[] = []; + function check() { + return Svg.checkmark_svg().SetStyle("width: 1.5em; display:inline-block;"); + } + + function nocheck() { + return Svg.no_checkmark_svg().SetStyle("width: 1.5em; display: inline-block;"); + } + const includeLocation = new CheckBox( - new Combine([Svg.checkmark, tr.fsIncludeCurrentLocation]), - new Combine([Svg.no_checkmark, tr.fsIncludeCurrentLocation]), + new Combine([check(), tr.fsIncludeCurrentLocation]), + new Combine([nocheck(), tr.fsIncludeCurrentLocation]), true ) optionCheckboxes.push(includeLocation); @@ -68,8 +76,8 @@ export class ShareScreen extends UIElement { return tr.fsIncludeCurrentBackgroundMap.Subs({name: layer?.name ?? ""}).Render(); })); const includeCurrentBackground = new CheckBox( - new Combine([Svg.checkmark, currentBackground]), - new Combine([Svg.no_checkmark, currentBackground]), + new Combine([check(), currentBackground]), + new Combine([nocheck(), currentBackground]), true ) optionCheckboxes.push(includeCurrentBackground); @@ -83,8 +91,8 @@ export class ShareScreen extends UIElement { const includeLayerChoices = new CheckBox( - new Combine([Svg.checkmark, tr.fsIncludeCurrentLayers]), - new Combine([Svg.no_checkmark, tr.fsIncludeCurrentLayers]), + new Combine([check(), tr.fsIncludeCurrentLayers]), + new Combine([nocheck(), tr.fsIncludeCurrentLayers]), true ) optionCheckboxes.push(includeLayerChoices); @@ -113,8 +121,8 @@ export class ShareScreen extends UIElement { for (const swtch of switches) { const checkbox = new CheckBox( - new Combine([Svg.checkmark, Translations.W(swtch.human)]), - new Combine([Svg.no_checkmark, Translations.W(swtch.human)]), !swtch.reverse + new Combine([check(), Translations.W(swtch.human)]), + new Combine([nocheck(), Translations.W(swtch.human)]), !swtch.reverse ); optionCheckboxes.push(checkbox); optionParts.push(checkbox.isEnabled.map((isEn) => { diff --git a/assets/svg/compass.svg b/assets/svg/compass.svg new file mode 100644 index 000000000..52f0b97f9 --- /dev/null +++ b/assets/svg/compass.svg @@ -0,0 +1,199 @@ + + + + + + + image/svg+xml + + + + + + + N + S + E + W + NW + SW + NE + SE + diff --git a/assets/svg/direction.svg b/assets/svg/direction.svg new file mode 100644 index 000000000..e1eccac39 --- /dev/null +++ b/assets/svg/direction.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 994a827f1..29d9e2755 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -96,6 +96,17 @@ } ] }, + { + "question": { + "en": "In which geographical direction does this camera film?", + "nl": "Naar welke geografische richting filmt deze camera?" + }, + "render": "Films to {camera:direction}", + "freeform": { + "key": "camera:direction", + "type": "direction" + } + }, { "freeform": { "key": "operator" diff --git a/index.css b/index.css index d5c4f874c..ab483a4da 100644 --- a/index.css +++ b/index.css @@ -26,6 +26,8 @@ a { svg { fill: var(--foreground-color) !important; stroke: var(--foreground-color) !important; + width: 100%; + height: 100%; } svg path { @@ -33,6 +35,10 @@ svg path { stroke: var(--foreground-color) !important; } +.direction-svg svg path{ + fill: var(--catch-detail-color) !important; +} + #leafletDiv { height: 100%; diff --git a/package-lock.json b/package-lock.json index b175d16c5..21245de45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1774,9 +1774,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001066", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz", - "integrity": "sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw==" + "version": "1.0.30001157", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001157.tgz", + "integrity": "sha512-gOerH9Wz2IRZ2ZPdMfBvyOi3cjaz4O4dgNwPGzx8EhqAs4+2IL/O+fJsbt+znSigujoZG8bVcIAUM/I/E5K3MA==" }, "canvas": { "version": "2.6.1", diff --git a/test.ts b/test.ts index f1b338122..e578eb8f1 100644 --- a/test.ts +++ b/test.ts @@ -1,10 +1,13 @@ //* -import Direction from "./UI/Input/Direction"; - -new Direction().AttachTo("maindiv") +import Direction from "./UI/Input/DirectionInput"; +import {UIEventSource} from "./Logic/UIEventSource"; +import {VariableUiElement} from "./UI/Base/VariableUIElement"; +const d = new UIEventSource(90); +new Direction(d).AttachTo("maindiv") +new VariableUiElement(d.map(d => ""+d+"°")).AttachTo("extradiv") /*/ From 1bd5411071b95df9e058d8ef13f7ddadde760fb3 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 03:22:41 +0100 Subject: [PATCH 09/20] Download logo --- UI/Input/DirectionInput.ts | 2 +- assets/themes/surveillance_cameras/logo.svg | 8 ++++++++ .../themes/surveillance_cameras/surveillance_cameras.json | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 assets/themes/surveillance_cameras/logo.svg diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index 7c6f15228..3d7f89a40 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -37,7 +37,7 @@ export default class DirectionInput extends InputElement { console.log("Inner render direction") return new Combine([ Svg.direction_svg().SetStyle( - `position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotategs:${this.value.data}deg;`) + `position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotate:${this.value.data}deg;`) .SetClass("direction-svg"), Svg.compass_svg().SetStyle( "position: absolute;top: 0;left: 0;width: 100%;height: 100%;") diff --git a/assets/themes/surveillance_cameras/logo.svg b/assets/themes/surveillance_cameras/logo.svg new file mode 100644 index 000000000..368714391 --- /dev/null +++ b/assets/themes/surveillance_cameras/logo.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 29d9e2755..67bd8c5aa 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -17,7 +17,7 @@ "nl" ], "maintainer": "", - "icon": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg", + "icon": "./assets/themes/surveillance_cameras/logo.svg", "version": "0", "startLat": 0, "startLon": 0, @@ -277,7 +277,7 @@ ], "hideUnderlayingFeaturesMinPercentage": 0, "icon": { - "render": "https://upload.wikimedia.org/wikipedia/commons/b/b7/Video_surveillance_logo.svg" + "render": "./assets/themes/surveillance_cameras/logo.svg" }, "width": { "render": "8" From e2881840da6c439c44adf2025cec1f42a9232add Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 14:12:31 +0100 Subject: [PATCH 10/20] Fix touch direction input --- UI/Input/DirectionInput.ts | 2 ++ test.ts | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index 3d7f89a40..d6399fd91 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -62,7 +62,9 @@ export default class DirectionInput extends InputElement { htmlElement.ontouchmove = (ev: TouchEvent) => { + console.log("Getting a touch", ev.touches[0].clientX, ev.touches[0].clientY) onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); + ev.preventDefault(); } let isDown = false; diff --git a/test.ts b/test.ts index e578eb8f1..87cb971bd 100644 --- a/test.ts +++ b/test.ts @@ -5,9 +5,15 @@ import Direction from "./UI/Input/DirectionInput"; import {UIEventSource} from "./Logic/UIEventSource"; import {VariableUiElement} from "./UI/Base/VariableUIElement"; -const d = new UIEventSource(90); +const d = new UIEventSource("90"); new Direction(d).AttachTo("maindiv") -new VariableUiElement(d.map(d => ""+d+"°")).AttachTo("extradiv") +new VariableUiElement(d.map(d => "" + d + "°")).AttachTo("extradiv") + +UIEventSource.Chronic(25, () => { + const degr = (Number(d.data) + 1) % 360; + d.setData(""+ degr); + return true; +}) /*/ From e8d176cfe4d9b1a6610c10e22b3225efbd671ac4 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 14:56:20 +0100 Subject: [PATCH 11/20] Create icons for surveillance cams --- State.ts | 2 +- Svg.ts | 2 +- UI/FullScreenMessageBoxHandler.ts | 7 +- assets/themes/surveillance_cameras/cam.svg | 61 +++++++++++++++ assets/themes/surveillance_cameras/dome.svg | 85 +++++++++++++++++++++ 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 assets/themes/surveillance_cameras/cam.svg create mode 100644 assets/themes/surveillance_cameras/dome.svg diff --git a/State.ts b/State.ts index 89aa91537..c4faf5593 100644 --- a/State.ts +++ b/State.ts @@ -22,7 +22,7 @@ export default class State { // The singleton of the global state public static state: State; - public static vNumber = "0.1.3"; + public static vNumber = "0.1.3-rc1"; // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/Svg.ts b/Svg.ts index 4850a0c71..f5a8d65fb 100644 --- a/Svg.ts +++ b/Svg.ts @@ -104,7 +104,7 @@ export default class Svg { public static help_svg() { return new FixedUiElement(Svg.help);} public static help_ui() { return new FixedUiElement(Svg.help_img);} - public static home = " " + public static home = " " public static home_img = Img.AsImageElement(Svg.home) public static home_svg() { return new FixedUiElement(Svg.home);} public static home_ui() { return new FixedUiElement(Svg.home_img);} diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index c6b7a1f1c..855eb6246 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -11,6 +11,7 @@ export class FullScreenMessageBox extends UIElement { private static readonly _toTheMap_height : string = "5em"; private readonly returnToTheMap: UIElement; + private _content: UIElement; constructor(onClear: (() => void)) { super(); @@ -67,10 +68,8 @@ export class FullScreenMessageBox extends UIElement { if (State.state.fullScreenMessage.data === undefined) { return ""; } - - const el = document.getElementById(this.id); - - const uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle( + this._content = State.state.fullScreenMessage.data; + const uielement = new Combine([this._content]).SetStyle( "display:block;" + "padding: 1em;" + "padding-bottom:6em;" + diff --git a/assets/themes/surveillance_cameras/cam.svg b/assets/themes/surveillance_cameras/cam.svg new file mode 100644 index 000000000..0fcce8424 --- /dev/null +++ b/assets/themes/surveillance_cameras/cam.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/themes/surveillance_cameras/dome.svg b/assets/themes/surveillance_cameras/dome.svg new file mode 100644 index 000000000..28d0e325d --- /dev/null +++ b/assets/themes/surveillance_cameras/dome.svg @@ -0,0 +1,85 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + From a144cf17f4cfe30c1ef5b8fb1278e8b2ad8339fd Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 15 Nov 2020 16:08:17 +0100 Subject: [PATCH 12/20] Small tweaks --- InitUiElements.ts | 2 +- Svg.ts | 2 +- UI/Input/ValidatedTextField.ts | 2 +- assets/themes/surveillance_cameras/cam.svg | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/InitUiElements.ts b/InitUiElements.ts index d97819788..7b660bf4d 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -290,7 +290,7 @@ export class InitUiElements { if (userdetails.csCount < State.userJourney.mapCompleteHelpUnlock) { return "" } - return Translations.t.general.aboutMapcomplete.Render(); + return new Combine([Translations.t.general.aboutMapcomplete, "
Version "+State.vNumber]).Render(); }, [Locale.language])) } ); diff --git a/Svg.ts b/Svg.ts index f5a8d65fb..4850a0c71 100644 --- a/Svg.ts +++ b/Svg.ts @@ -104,7 +104,7 @@ export default class Svg { public static help_svg() { return new FixedUiElement(Svg.help);} public static help_ui() { return new FixedUiElement(Svg.help_img);} - public static home = " " + public static home = " " public static home_img = Img.AsImageElement(Svg.home) public static home_svg() { return new FixedUiElement(Svg.home);} public static home_ui() { return new FixedUiElement(Svg.home_img);} diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 9ba1a18a3..0071ab3e0 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -100,7 +100,7 @@ export default class ValidatedTextField { }), ValidatedTextField.tp( "direction", - "A geographical direction, in degrees. 0° is north, 90° is east", + "A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)", (str) => { str = "" + str; return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 && Number(str) <= 360 diff --git a/assets/themes/surveillance_cameras/cam.svg b/assets/themes/surveillance_cameras/cam.svg index 0fcce8424..dbe448f33 100644 --- a/assets/themes/surveillance_cameras/cam.svg +++ b/assets/themes/surveillance_cameras/cam.svg @@ -39,19 +39,19 @@ inkscape:window-height="1001" id="namedview8" showgrid="false" - inkscape:zoom="3.1466667" - inkscape:cx="77.828572" - inkscape:cy="166.9393" + inkscape:zoom="1.5733333" + inkscape:cx="-95.73727" + inkscape:cy="204.79067" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" /> + sodipodi:nodetypes="cccccccccccssccc" /> Date: Sun, 15 Nov 2020 19:17:36 +0100 Subject: [PATCH 13/20] Tweaks to icons, add direction to benches, tweak to direction input --- UI/FullScreenMessageBoxHandler.ts | 4 +- UI/Input/DirectionInput.ts | 6 +- assets/themes/benches/benches.json | 12 +++- assets/themes/surveillance_cameras/cam.svg | 18 ++--- assets/themes/surveillance_cameras/dome.svg | 24 +++---- assets/themes/surveillance_cameras/logo.svg | 69 ++++++++++++++++--- .../surveillance_cameras.json | 11 ++- 7 files changed, 103 insertions(+), 41 deletions(-) diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index 855eb6246..b94595e31 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -39,14 +39,14 @@ export class FullScreenMessageBox extends UIElement { this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) - .SetStyle("background:#7ebc6f;" + + .SetStyle("background:var(--catch-detail-color);" + "position: fixed;" + "z-index: 10000;" + "bottom: 0;" + "left: 0;" + `height: ${FullScreenMessageBox._toTheMap_height};` + "width: 100vw;" + - "color: white;" + + "color: var(--catch-detail-color-contrast);" + "font-weight: bold;" + "pointer-events: all;" + "cursor: pointer;" + diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index d6399fd91..f5c5c434f 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -62,7 +62,11 @@ export default class DirectionInput extends InputElement { htmlElement.ontouchmove = (ev: TouchEvent) => { - console.log("Getting a touch", ev.touches[0].clientX, ev.touches[0].clientY) + onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); + ev.preventDefault(); + } + + htmlElement.ontouchstart = (ev: TouchEvent) => { onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); ev.preventDefault(); } diff --git a/assets/themes/benches/benches.json b/assets/themes/benches/benches.json index 8945da34c..fef025e89 100644 --- a/assets/themes/benches/benches.json +++ b/assets/themes/benches/benches.json @@ -190,6 +190,16 @@ "fr": "De quel matériau ce banc est-il fait ?" } }, + { + "question": { + "en": "In which direction are you looking when sitting on the bench?" + }, + "render": "When sitting on the bench, one looks towards {direction}*", + "freeform": { + "key": "direction", + "type": "direction" + } + }, { "render": { "en": "Colour: {colour}", @@ -297,7 +307,7 @@ "render": "8" }, "iconSize": { - "render": "20,20,center" + "render": "30,30,center" }, "color": { "render": "#00f" diff --git a/assets/themes/surveillance_cameras/cam.svg b/assets/themes/surveillance_cameras/cam.svg index dbe448f33..d4f54e8b2 100644 --- a/assets/themes/surveillance_cameras/cam.svg +++ b/assets/themes/surveillance_cameras/cam.svg @@ -39,23 +39,17 @@ inkscape:window-height="1001" id="namedview8" showgrid="false" - inkscape:zoom="1.5733333" - inkscape:cx="-95.73727" - inkscape:cy="204.79067" + inkscape:zoom="4.4500586" + inkscape:cx="52.470633" + inkscape:cy="172.70315" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" /> - + style="fill:#000000;stroke:#ffffff;stroke-width:15;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="cccccccssccc" /> diff --git a/assets/themes/surveillance_cameras/dome.svg b/assets/themes/surveillance_cameras/dome.svg index 28d0e325d..389759d7c 100644 --- a/assets/themes/surveillance_cameras/dome.svg +++ b/assets/themes/surveillance_cameras/dome.svg @@ -7,8 +7,8 @@ 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="292.05588" - height="159.1156" + width="300" + height="300" version="1.1" id="svg6" sodipodi:docname="dome.svg" @@ -40,33 +40,27 @@ id="namedview8" showgrid="false" inkscape:zoom="1.5733334" - inkscape:cx="37.492305" - inkscape:cy="-39.615653" + inkscape:cx="84.526201" + inkscape:cy="188.2981" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" /> - - - - - \ No newline at end of file + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json index 67bd8c5aa..c5021203d 100644 --- a/assets/themes/surveillance_cameras/surveillance_cameras.json +++ b/assets/themes/surveillance_cameras/surveillance_cameras.json @@ -102,6 +102,7 @@ "nl": "Naar welke geografische richting filmt deze camera?" }, "render": "Films to {camera:direction}", + "condition": "camera:type!=dome", "freeform": { "key": "camera:direction", "type": "direction" @@ -277,13 +278,19 @@ ], "hideUnderlayingFeaturesMinPercentage": 0, "icon": { - "render": "./assets/themes/surveillance_cameras/logo.svg" + "render": "./assets/themes/surveillance_cameras/cam.svg", + "mappings": [ + { + "if": "camera:type=dome", + "then": "./assets/themes/surveillance_cameras/dome.svg" + } + ] }, "width": { "render": "8" }, "iconSize": { - "render": "30,30,center" + "render": "50,50,center" }, "color": { "render": "#00f" From 314894085a56300f0e33c9f2bacc13ee78ba3cd0 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 16 Nov 2020 01:59:30 +0100 Subject: [PATCH 14/20] Cleaning filtered layer --- Customizations/JSON/LayerConfig.ts | 59 ++++ InitUiElements.ts | 18 +- Logic/FilteredLayer.ts | 344 ++++++++------------ UI/FullScreenMessageBoxHandler.ts | 31 +- UI/Input/DirectionInput.ts | 1 - UI/Popup/EditableTagRendering.ts | 28 +- UI/Popup/TagRenderingQuestion.ts | 3 - UI/SimpleAddUI.ts | 3 +- assets/themes/surveillance_cameras/dome.svg | 10 +- css/mobile.css | 9 +- index.css | 4 +- package.json | 2 +- 12 files changed, 232 insertions(+), 280 deletions(-) diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts index 7b610b19b..268fa9817 100644 --- a/Customizations/JSON/LayerConfig.ts +++ b/Customizations/JSON/LayerConfig.ts @@ -116,4 +116,63 @@ export default class LayerConfig { } + + + public GenerateLeafletStyle(tags: any): + { + color: string; + icon: { popupAnchor: [number, number]; iconAnchor: [number, number]; iconSize: [number, number]; iconUrl: string }; weight: number; dashArray: number[] + } { + const iconUrl = this.icon?.GetRenderValue(tags)?.txt; + const iconSize = (this.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(","); + + + const dashArray = this.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number); + + function num(str, deflt = 40) { + const n = Number(str); + if (isNaN(n)) { + return deflt; + } + return n; + } + + const iconW = num(iconSize[0]); + const iconH = num(iconSize[1]); + const mode = iconSize[2] ?? "center" + + let anchorW = iconW / 2; + let anchorH = iconH / 2; + if (mode === "left") { + anchorW = 0; + } + if (mode === "right") { + anchorW = iconW; + } + + if (mode === "top") { + anchorH = 0; + } + if (mode === "bottom") { + anchorH = iconH; + } + + + const color = this.color?.GetRenderValue(tags)?.txt ?? "#00f"; + let weight = num(this.width?.GetRenderValue(tags)?.txt, 5); + return { + icon: + { + iconUrl: iconUrl, + iconSize: [iconW, iconH], + iconAnchor: [anchorW, anchorH], + popupAnchor: [0, 3 - anchorH] + }, + color: color, + weight: weight, + dashArray: dashArray + }; + } + + } \ No newline at end of file diff --git a/InitUiElements.ts b/InitUiElements.ts index 7b660bf4d..20af0c885 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -475,18 +475,16 @@ export class InitUiElements { throw "Layer " + layer + " was not substituted"; } - const generateInfo = (tagsES) => { - - return new FeatureInfoBox( - tagsES, - layer, - ) - }; - - const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo); + const flayer: FilteredLayer = new FilteredLayer(layer, + (tagsES) => { + return new FeatureInfoBox( + tagsES, + layer, + ) + }); flayers.push(flayer); - QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer "+layer.id+" is shown") + QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer " + layer.id + " is shown") .map((str) => str !== "false", [], (b) => b.toString()) .syncWith( flayer.isDisplayed diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts index 5e0a106c5..12522ab02 100644 --- a/Logic/FilteredLayer.ts +++ b/Logic/FilteredLayer.ts @@ -25,13 +25,9 @@ export class FilteredLayer { public readonly layerDef: LayerConfig; private readonly _maxAllowedOverlap: number; - private readonly _style: (properties) => { color: string, weight?: number, icon: { iconUrl: string, iconSize?: [number, number], popupAnchor?: [number, number], iconAnchor?: [number, number] } }; - - /** The featurecollection from overpass */ private _dataFromOverpass: any[]; - private readonly _wayHandling: number; /** List of new elements, geojson features */ private _newElements = []; @@ -49,60 +45,7 @@ export class FilteredLayer { ) { this.layerDef = layerDef; - this._wayHandling = layerDef.wayHandling; this._showOnPopup = showOnPopup; - this._style = (tags) => { - - const iconUrl = layerDef.icon?.GetRenderValue(tags)?.txt; - const iconSize = (layerDef.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(","); - - - const dashArray = layerDef.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number); - - function num(str, deflt = 40) { - const n = Number(str); - if (isNaN(n)) { - return deflt; - } - return n; - } - - const iconW = num(iconSize[0]); - const iconH = num(iconSize[1]); - const mode = iconSize[2] ?? "center" - - let anchorW = iconW / 2; - let anchorH = iconH / 2; - if (mode === "left") { - anchorW = 0; - } - if (mode === "right") { - anchorW = iconW; - } - - if (mode === "top") { - anchorH = 0; - } - if (mode === "bottom") { - anchorH = iconH; - } - - - const color = layerDef.color?.GetRenderValue(tags)?.txt ?? "#00f"; - let weight = num(layerDef.width?.GetRenderValue(tags)?.txt, 5); - return { - icon: - { - iconUrl: iconUrl, - iconSize: [iconW, iconH], - iconAnchor: [anchorW, anchorH], - popupAnchor: [0, 3 - anchorH] - }, - color: color, - weight: weight, - dashArray: dashArray - }; - }; this.name = name; this.filters = layerDef.overpassTags; this._maxAllowedOverlap = layerDef.hideUnderlayingFeaturesMinPercentage; @@ -123,13 +66,6 @@ export class FilteredLayer { } }) } - - static fromDefinition(definition, showOnPopup: (tags: UIEventSource, feature: any) => UIElement): FilteredLayer { - return new FilteredLayer(definition, showOnPopup); - - } - - /** * The main function to load data into this layer. * The data that is NOT used by this layer, is returned as a geojson object; the other data is rendered @@ -138,32 +74,15 @@ export class FilteredLayer { const leftoverFeatures = []; const selfFeatures = []; for (let feature of geojson.features) { - // feature.properties contains all the properties - const tags = TagUtils.proprtiesToKV(feature.properties); - if (!this.filters.matches(tags)) { leftoverFeatures.push(feature); continue; } - - if (feature.geometry.type !== "Point") { - const centerPoint = GeoOperations.centerpoint(feature); - if (this._wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) { - selfFeatures.push(centerPoint); - } else if (this._wayHandling === LayerConfig.WAYHANDLING_CENTER_ONLY) { - feature = centerPoint; - } - } selfFeatures.push(feature); - } - - this.RenderLayer({ - type: "FeatureCollection", - features: selfFeatures - }) + this.RenderLayer(selfFeatures) const notShadowed = []; for (const feature of leftoverFeatures) { @@ -186,18 +105,140 @@ export class FilteredLayer { public AddNewElement(element) { this._newElements.push(element); - this.RenderLayer({features: this._dataFromOverpass}, element); // Update the layer - + this.RenderLayer( this._dataFromOverpass); // Update the layer } - private RenderLayer(data, openPopupOf = undefined) { - let self = this; + private RenderLayer(features) { if (this._geolayer !== undefined && this._geolayer !== null) { // Remove the old geojson layer from the map - we'll reshow all the elements later on anyway State.state.bm.map.removeLayer(this._geolayer); } + // We fetch all the data we have to show: + let fusedFeatures = this.ApplyWayHandling(this.FuseData(features)); + console.log("Fused:",fusedFeatures) + + // And we copy some features as points - if needed + const data = { + type: "FeatureCollection", + features: fusedFeatures + } + + let self = this; + console.log(data); + this._geolayer = L.geoJSON(data, { + /* style: feature => { + self.layerDef.GenerateLeafletStyle(feature.properties); + return { + color: "#f00", + weight: 4 + } + },*/ + /* + pointToLayer: function (feature, latLng) { + // Point to layer converts the 'point' to a layer object - as the geojson layer natively cannot handle points + // Click handling is done in the next step + + const style = self.layerDef.GenerateLeafletStyle(feature.properties); + let marker; + if (style.icon === undefined) { + marker = L.circle(latLng, { + radius: 25, + color: style.color + }); + } else if (style.icon.iconUrl.startsWith("$circle")) { + marker = L.circle(latLng, { + radius: 25, + color: style.color + }); + } else { + if (style.icon.iconSize === undefined) { + style.icon.iconSize = [50, 50] + } + + marker = L.marker(latLng, { + icon: L.icon(style.icon) + }); + } + return marker; + },*/ +/* + onEachFeature: function (feature, layer:Layer) { + + layer.on("click", (e) => { + if (layer.getPopup() === undefined + && (window.screen.availHeight > 600 || window.screen.availWidth > 600) // We DON'T trigger this code on small screens! No need to create a popup + ) { + const popup = L.popup({ + autoPan: true, + closeOnEscapeKey: true, + }, layer); + + // @ts-ignore + popup.setLatLng(e.latlng) + + layer.bindPopup(popup); + const eventSource = State.state.allElements.addOrGetElement(feature); + const uiElement = self._showOnPopup(eventSource, feature); + // We first render the UIelement (which'll still need an update later on...) + // But at least it'll be visible already + popup.setContent(uiElement.Render()); + popup.openOn(State.state.bm.map); + // popup.openOn(State.state.bm.map); + // ANd we perform the pending update + uiElement.Update(); + } + // We set the element as selected... + State.state.selectedElement.setData(feature); + + // We mark the event as consumed + L.DomEvent.stop(e); + }); + } + */ + } + ) + ; + + if (this.combinedIsDisplayed.data) { + this._geolayer.addTo(State.state.bm.map); + } + + } + + private ApplyWayHandling(fusedFeatures: any[]) { + if (this.layerDef.wayHandling === LayerConfig.WAYHANDLING_DEFAULT) { + // We don't have to do anything special + return fusedFeatures; + } + + + // We have to convert all the ways into centerpoints + const existingPoints = []; + const newPoints = []; + const existingWays = []; + + for (const feature of fusedFeatures) { + if (feature.geometry.type === "Point") { + existingPoints.push(feature); + continue; + } + + existingWays.push(feature); + const centerPoint = GeoOperations.centerpoint(feature); + newPoints.push(centerPoint); + } + + fusedFeatures = existingPoints.concat(newPoints); + if (this.layerDef.wayHandling === LayerConfig.WAYHANDLING_CENTER_AND_WAY) { + fusedFeatures = fusedFeatures.concat(existingWays) + } + return fusedFeatures; + } + + //*Fuses the old and the new datasets*/ + private FuseData(data: any[]) { const oldData = this._dataFromOverpass ?? []; // We keep track of all the ids that are freshly loaded in order to avoid adding duplicates @@ -205,7 +246,7 @@ export class FilteredLayer { // A list of all the features to show const fusedFeatures = []; // First, we add all the fresh data: - for (const feature of data.features) { + for (const feature of data) { idsFromOverpass.add(feature.properties.id); fusedFeatures.push(feature); } @@ -226,133 +267,6 @@ export class FilteredLayer { fusedFeatures.push(feature); } } - - - // We use a new, fused dataset - data = { - type: "FeatureCollection", - features: fusedFeatures - } - - - // The data is split in two parts: the point and the rest - // The points get a special treatment in order to render them properly - // Note that some features might get a point representation as well - - const runWhenAdded: (() => void)[] = [] - - this._geolayer = L.geoJSON(data, { - style: function (feature) { - return self._style(feature.properties); - }, - pointToLayer: function (feature, latLng) { - const style = self._style(feature.properties); - let marker; - if (style.icon === undefined) { - marker = L.circle(latLng, { - radius: 25, - color: style.color - }); - - } else if (style.icon.iconUrl.startsWith("$circle")) { - marker = L.circle(latLng, { - radius: 25, - color: style.color - }); - } else { - if (style.icon.iconSize === undefined) { - style.icon.iconSize = [50, 50] - } - - // @ts-ignore - marker = L.marker(latLng, { - icon: L.icon(style.icon), - }); - } - let eventSource = State.state.allElements.addOrGetElement(feature); - const popup = L.popup({}, marker); - let uiElement: UIElement; - let content = undefined; - let p = marker.bindPopup(popup) - .on("popupopen", () => { - if (content === undefined) { - uiElement = self._showOnPopup(eventSource, feature); - // Lazily create the content - content = uiElement.Render(); - } - popup.setContent(content); - uiElement.Update(); - }); - - if (feature === openPopupOf) { - runWhenAdded.push(() => { - p.openPopup(); - }) - } - - return marker; - }, - - onEachFeature: function (feature, layer:Layer) { - - // We monky-patch the feature element with an update-style - function updateStyle () { - // @ts-ignore - if (layer.setIcon) { - const style = self._style(feature.properties); - const icon = style.icon; - if (icon.iconUrl) { - if (icon.iconUrl.startsWith("$circle")) { - // pass - } else { - // @ts-ignore - layer.setIcon(L.icon(icon)) - } - } - } else { - self._geolayer.setStyle(function (featureX) { - return self._style(featureX.properties); - }); - } - } - - let eventSource = State.state.allElements.addOrGetElement(feature); - - - eventSource.addCallback(updateStyle); - - function openPopup(e) { - if (feature.geometry.type === "Point") { - return; // Points bind their own popups - } - const uiElement = self._showOnPopup(eventSource, feature); - L.popup({ - autoPan: true, - }).setContent(uiElement.Render()) - .setLatLng(e.latlng) - .openOn(State.state.bm.map); - uiElement.Update(); - if (e) { - L.DomEvent.stop(e); // Marks the event as consumed - } - } - - layer.on("click", (e) => { - updateStyle(); - openPopup(e); - State.state.selectedElement.setData(feature); - - }); - } - }); - - if (this.combinedIsDisplayed.data) { - this._geolayer.addTo(State.state.bm.map); - for (const f of runWhenAdded) { - f(); - } - } + return fusedFeatures; } - - } \ No newline at end of file diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts index b94595e31..d1e935556 100644 --- a/UI/FullScreenMessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -8,34 +8,13 @@ import Combine from "./Base/Combine"; */ export class FullScreenMessageBox extends UIElement { - private static readonly _toTheMap_height : string = "5em"; - private readonly returnToTheMap: UIElement; private _content: UIElement; constructor(onClear: (() => void)) { - super(); + super(State.state.fullScreenMessage); this.HideOnEmpty(true); const self = this; - State.state.fullScreenMessage.addCallbackAndRun(uiElement => { - self.Update(); - if (uiElement === undefined) { - location.hash = ""; - } else { - // The 'hash' makes sure a new piece of history is added. This makes the 'back-button' on android remove the popup - location.hash = "#element"; - } - }); - - if (window !== undefined) { - window.onhashchange = function () { - if (location.hash === "") { - // No more element: back to the map! - State.state.fullScreenMessage.setData(undefined); - onClear(); - } - } - } this.returnToTheMap = new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")]) @@ -44,7 +23,7 @@ export class FullScreenMessageBox extends UIElement { "z-index: 10000;" + "bottom: 0;" + "left: 0;" + - `height: ${FullScreenMessageBox._toTheMap_height};` + + `height: var(--return-to-the-map-height);` + "width: 100vw;" + "color: var(--catch-detail-color-contrast);" + "font-weight: bold;" + @@ -55,10 +34,8 @@ export class FullScreenMessageBox extends UIElement { "padding-bottom: 1.2em;" + "box-sizing:border-box") .onClick(() => { - console.log("Returning...") State.state.fullScreenMessage.setData(undefined); onClear(); - self.Update(); }); } @@ -73,9 +50,9 @@ export class FullScreenMessageBox extends UIElement { "display:block;" + "padding: 1em;" + "padding-bottom:6em;" + - `margin-bottom:${FullScreenMessageBox._toTheMap_height};` + + `margin-bottom: var(--return-to-the-map-height);` + "box-sizing:border-box;" + - `height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});` + + `height:calc(100vh - var(--return-to-the-map-height));` + "overflow-y: auto;" + "max-width:100vw;" + "overflow-x:hidden;" + diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts index f5c5c434f..7188ad26e 100644 --- a/UI/Input/DirectionInput.ts +++ b/UI/Input/DirectionInput.ts @@ -68,7 +68,6 @@ export default class DirectionInput extends InputElement { htmlElement.ontouchstart = (ev: TouchEvent) => { onPosChange(ev.touches[0].clientX, ev.touches[0].clientY); - ev.preventDefault(); } let isDown = false; diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index fce78e676..b8e9d5bed 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -27,24 +27,25 @@ export default class EditableTagRendering extends UIElement { this.ListenTo(this._editMode); this.ListenTo(State.state?.osmConnection?.userDetails) - const self = this; - this._answer = new TagRenderingAnswer(tags, configuration); - this._answer.SetStyle("width:100%;") if (this._configuration.question !== undefined) { - // 2.3em total width - if(State.state.featureSwitchUserbadge.data){ - - this._editButton = - Svg.pencil_svg().SetClass("edit-button") - .onClick(() => { - self._editMode.setData(true); - }); + if (State.state.featureSwitchUserbadge.data) { + // 2.3em total width + const self = this; + this._editButton = + Svg.pencil_svg().SetClass("edit-button") + .onClick(() => { + self._editMode.setData(true); + }); } + } + } - + private GenerateQuestion() { + const self = this; + if (this._configuration.question !== undefined) { // And at last, set up the skip button const cancelbutton = Translations.t.general.cancel.Clone() @@ -53,7 +54,7 @@ export default class EditableTagRendering extends UIElement { self._editMode.setData(false) }); - this._question = new TagRenderingQuestion(tags, configuration, + return new TagRenderingQuestion(this._tags, this._configuration, () => { self._editMode.setData(false) }, @@ -65,6 +66,7 @@ export default class EditableTagRendering extends UIElement { InnerRender(): string { if (this._editMode.data) { + this._question = this.GenerateQuestion(); return this._question.Render(); } diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 10ba6bc03..d80342911 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -118,7 +118,6 @@ export default class TagRenderingQuestion extends UIElement { const inputEl = new InputElementMap( checkBoxes, (t0, t1) => { - console.log("IsEquiv?",t0, t1, t0?.isEquivalent(t1)) return t0?.isEquivalent(t1) ?? false }, (indices) => { @@ -162,8 +161,6 @@ export default class TagRenderingQuestion extends UIElement { } } - console.log(indices, freeformExtras); - if (freeformField) { if (freeformExtras.length > 0) { freeformField.GetValue().setData(new Tag(this._configuration.freeform.key, freeformExtras.join(";"))); diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index c4acc5f46..db1c6000d 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -8,7 +8,6 @@ import Locale from "./i18n/Locale"; import State from "../State"; import {UIEventSource} from "../Logic/UIEventSource"; -import {Img} from "./Img"; import Svg from "../Svg"; /** @@ -115,8 +114,8 @@ export class SimpleAddUI extends UIElement { const loc = State.state.bm.LastClickLocation.data; let feature = State.state.changes.createElement(tags, loc.lat, loc.lon); - layerToAddTo.AddNewElement(feature); State.state.selectedElement.setData(feature); + layerToAddTo.AddNewElement(feature); } } diff --git a/assets/themes/surveillance_cameras/dome.svg b/assets/themes/surveillance_cameras/dome.svg index 389759d7c..ce0f5c0d5 100644 --- a/assets/themes/surveillance_cameras/dome.svg +++ b/assets/themes/surveillance_cameras/dome.svg @@ -40,27 +40,27 @@ id="namedview8" showgrid="false" inkscape:zoom="1.5733334" - inkscape:cx="84.526201" + inkscape:cx="-20.982269" inkscape:cy="188.2981" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg6" />