diff --git a/assets/layers/icons/icons.json b/assets/layers/icons/icons.json index 69c52e217..9c5fc0b78 100644 --- a/assets/layers/icons/icons.json +++ b/assets/layers/icons/icons.json @@ -67,6 +67,14 @@ ], "metacondition": "__showTimeSensitiveIcons!=no" }, + {"id":"open_until", + "labels": ["defaults","in_favourite"], + "#":"Titleicon showing 'open until 17:00'", + "icon": { + "class": "w-20 mx-1 flex items-center" + }, + "render": "{opening_hours_state()}" + }, { "id": "phonelink", "labels": [ diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index 13d12ea52..ae394847b 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -745,6 +745,10 @@ video { top: 2.5rem; } +.left-1\/4 { + left: 25%; +} + .isolate { isolation: isolate; } @@ -1088,6 +1092,10 @@ video { height: 2.75rem; } +.h-5 { + height: 1.25rem; +} + .h-48 { height: 12rem; } @@ -1198,6 +1206,14 @@ video { width: 50%; } +.w-14 { + width: 3.5rem; +} + +.w-5 { + width: 1.25rem; +} + .w-10 { width: 2.5rem; } @@ -1289,8 +1305,8 @@ video { appearance: none; } -.grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid-cols-1 { @@ -1453,10 +1469,6 @@ video { justify-self: end; } -.justify-self-center { - justify-self: center; -} - .overflow-auto { overflow: auto; } diff --git a/src/UI/Favourites/FavouriteSummary.svelte b/src/UI/Favourites/FavouriteSummary.svelte index 7cc402381..112c3dd69 100644 --- a/src/UI/Favourites/FavouriteSummary.svelte +++ b/src/UI/Favourites/FavouriteSummary.svelte @@ -6,7 +6,6 @@ import { ImmutableStore } from "../../Logic/UIEventSource"; import { GeoOperations } from "../../Logic/GeoOperations"; import Center from "../../assets/svg/Center.svelte"; - import { Utils } from "../../Utils"; export let feature: Feature; let properties: Record = feature.properties; @@ -16,49 +15,49 @@ const favLayer = state.layerState.filteredLayers.get("favourite"); const favConfig = favLayer.layerDef; const titleConfig = favConfig.title; - - function center(){ - const [lon, lat] = GeoOperations.centerpointCoordinates(feature) + + function center() { + const [lon, lat] = GeoOperations.centerpointCoordinates(feature); state.mapProperties.location.setData( - {lon, lat} - ) - state.guistate.menuIsOpened.setData(false) + { lon, lat } + ); + state.guistate.menuIsOpened.setData(false); } - - function select(){ - state.selectedLayer.setData(favConfig) - state.selectedElement.setData(feature) - center() + + function select() { + state.selectedLayer.setData(favConfig); + state.selectedElement.setData(feature); + center(); } - - const coord = GeoOperations.centerpointCoordinates(feature) - const distance = state.mapProperties.location.stabilized(500).mapD(({lon, lat}) => { - let meters = Math.round(GeoOperations.distanceBetween(coord, [lon, lat])) - - if(meters < 1000){ - return meters +"m" + + const coord = GeoOperations.centerpointCoordinates(feature); + const distance = state.mapProperties.location.stabilized(500).mapD(({ lon, lat }) => { + let meters = Math.round(GeoOperations.distanceBetween(coord, [lon, lat])); + + if (meters < 1000) { + return meters + "m"; } - - meters = Math.round(meters / 100) - const kmStr = ""+meters - - - return kmStr.substring(0, kmStr.length - 1)+"."+kmStr.substring(kmStr.length-1) +"km" - }) - const titleIconBlacklist = ["osmlink","sharelink","favourite_title_icon"] + + meters = Math.round(meters / 100); + const kmStr = "" + meters; + + + return kmStr.substring(0, kmStr.length - 1) + "." + kmStr.substring(kmStr.length - 1) + "km"; + }); + const titleIconBlacklist = ["osmlink", "sharelink", "favourite_title_icon"]; -
- - {$distance} - + diff --git a/src/UI/OpeningHours/NextChangeViz.svelte b/src/UI/OpeningHours/NextChangeViz.svelte new file mode 100644 index 000000000..cdafa8903 --- /dev/null +++ b/src/UI/OpeningHours/NextChangeViz.svelte @@ -0,0 +1,50 @@ + + +{#if $currentState !== undefined} +
+ {#if $currentState === true} + + + {:else if $currentState === false} + + + {/if} + + {#if $nextChange !== undefined} + + {$nextChange} + + {/if} + +
+ +{/if} diff --git a/src/UI/OpeningHours/OpeningHours.ts b/src/UI/OpeningHours/OpeningHours.ts index cd62fdd79..400f44c56 100644 --- a/src/UI/OpeningHours/OpeningHours.ts +++ b/src/UI/OpeningHours/OpeningHours.ts @@ -1,5 +1,6 @@ import { Utils } from "../../Utils" import opening_hours from "opening_hours" +import { Store } from "../../Logic/UIEventSource" export interface OpeningHour { weekday: number // 0 is monday, 1 is tuesday, ... @@ -494,10 +495,48 @@ This list will be sorted return [changeHours, changeHourText] } + public static CreateOhObjectStore( + tags: Store>, + key: string = "opening_hours", + prefixToIgnore?: string, + postfixToIgnore?: string + ): Store { + prefixToIgnore ??= "" + postfixToIgnore ??= "" + const country = tags.map((tags) => tags._country) + return tags + .mapD((tags) => { + const value: string = tags[key] + if (value === undefined) { + return undefined + } + if ( + (prefixToIgnore || postfixToIgnore) && + value.startsWith(prefixToIgnore) && + value.endsWith(postfixToIgnore) + ) { + return value + .substring(prefixToIgnore.length, value.length - postfixToIgnore.length) + .trim() + } + return value + }) + .mapD( + (ohtext) => { + try { + return OH.CreateOhObject(tags.data, ohtext, country.data) + } catch (e) { + return "error" + } + }, + [country] + ) + } public static CreateOhObject( tags: Record & { _lat: number; _lon: number; _country?: string }, - textToParse: string + textToParse: string, + country?: string ) { // noinspection JSPotentiallyInvalidConstructorUsage return new opening_hours( @@ -506,7 +545,7 @@ This list will be sorted lat: tags._lat, lon: tags._lon, address: { - country_code: tags._country?.toLowerCase(), + country_code: country.toLowerCase(), state: undefined, }, }, diff --git a/src/UI/OpeningHours/OpeningHoursVisualization.ts b/src/UI/OpeningHours/OpeningHoursVisualization.ts index f51c225df..560a2276d 100644 --- a/src/UI/OpeningHours/OpeningHoursVisualization.ts +++ b/src/UI/OpeningHours/OpeningHoursVisualization.ts @@ -3,7 +3,6 @@ import Combine from "../Base/Combine" import { FixedUiElement } from "../Base/FixedUiElement" import { OH } from "./OpeningHours" import Translations from "../i18n/Translations" -import Constants from "../../Models/Constants" import BaseUIElement from "../BaseUIElement" import Toggle from "../Input/Toggle" import { VariableUiElement } from "../Base/VariableUIElement" @@ -30,48 +29,20 @@ export default class OpeningHoursVisualization extends Toggle { prefix = "", postfix = "" ) { - const country = tags.map((tags) => tags._country) + const openingHoursStore = OH.CreateOhObjectStore(tags, key, prefix, postfix) const ohTable = new VariableUiElement( - tags - .map((tags) => { - const value: string = tags[key] - if (value === undefined) { - return undefined - } - if (value.startsWith(prefix) && value.endsWith(postfix)) { - return value.substring(prefix.length, value.length - postfix.length).trim() - } - return value - }) // This mapping will absorb all other changes to tags in order to prevent regeneration - .map( - (ohtext) => { - if (ohtext === undefined) { - return new FixedUiElement( - "No opening hours defined with key " + key - ).SetClass("alert") - } - try { - return OpeningHoursVisualization.CreateFullVisualisation( - OH.CreateOhObject(tags.data, ohtext) - ) - } catch (e) { - console.warn(e, e.stack) - return new Combine([ - Translations.t.general.opening_hours.error_loading, - new Toggle( - new FixedUiElement(e).SetClass("subtle"), - undefined, - state?.osmConnection?.userDetails.map( - (userdetails) => - userdetails.csCount >= - Constants.userJourney.tagsVisibleAndWikiLinked - ) - ), - ]) - } - }, - [country] - ) + openingHoursStore.map((opening_hours_obj) => { + if (opening_hours_obj === undefined) { + return new FixedUiElement("No opening hours defined with key " + key).SetClass( + "alert" + ) + } + + if (opening_hours_obj === "error") { + return Translations.t.general.opening_hours.error_loading + } + return OpeningHoursVisualization.CreateFullVisualisation(opening_hours_obj) + }) ) super( diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index ce09eebe4..7bfdf0b93 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -81,6 +81,7 @@ import LogoutButton from "./Base/LogoutButton.svelte" import OpenJosm from "./Base/OpenJosm.svelte" import MarkAsFavourite from "./Popup/MarkAsFavourite.svelte" import MarkAsFavouriteMini from "./Popup/MarkAsFavouriteMini.svelte" +import NextChangeViz from "./OpeningHours/NextChangeViz.svelte" class NearbyImageVis implements SpecialVisualization { // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests @@ -827,6 +828,46 @@ export default class SpecialVisualizations { ) }, }, + { + funcName: "opening_hours_state", + docs: "A small element, showing if the POI is currently open and when the next change is", + args: [ + { + name: "key", + defaultValue: "opening_hours", + doc: "The tagkey from which the opening hours are read.", + }, + { + name: "prefix", + defaultValue: "", + doc: "Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__", + }, + { + name: "postfix", + defaultValue: "", + doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__", + }, + ], + needsUrls: [], + constr( + state: SpecialVisualizationState, + tags: UIEventSource>, + args: string[], + feature: Feature, + layer: LayerConfig + ): BaseUIElement { + const keyToUse = args[0] + const prefix = args[1] + const postfix = args[2] + return new SvelteUIElement(NextChangeViz, { + state, + keyToUse, + tags, + prefix, + postfix, + }) + }, + }, { funcName: "canonical", needsUrls: [],