forked from MapComplete/MapComplete
		
	Review of many feature switches and iframe context; fix OH-vis when ranges are not given
This commit is contained in:
		
							parent
							
								
									ecfa7d3d1d
								
							
						
					
					
						commit
						afea9adacb
					
				
					 21 changed files with 157 additions and 88 deletions
				
			
		|  | @ -292,6 +292,7 @@ | |||
|           } | ||||
|         } | ||||
|       }, | ||||
|       "metacondition": "_loggedIn=true", | ||||
|       "mappings": [ | ||||
|         { | ||||
|           "if": "id~.*/-.*", | ||||
|  |  | |||
|  | @ -17,8 +17,7 @@ | |||
|       "description": "This block shows the known images which are linked with the `image`-keys, but also via `mapillary` and `wikidata` and shows the button to upload new images", | ||||
|       "render": { | ||||
|         "*": "{image_carousel()}{image_upload()}" | ||||
|       }, | ||||
|       "classes": "my-4" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "id": "mapillary", | ||||
|  |  | |||
|  | @ -325,7 +325,7 @@ | |||
|         "opening_hours": { | ||||
|             "all_days_from": "Opened every day {ranges}", | ||||
|             "closed_permanently": "Closed for an unknown duration", | ||||
|             "closed_until": "Closed until {date}", | ||||
|             "closed_until": "Opens at {date}", | ||||
|             "error": "Could not parse the opening hours", | ||||
|             "error_loading": "Error: could not visualize these opening hours.", | ||||
|             "friday": "On friday {ranges}", | ||||
|  | @ -359,6 +359,7 @@ | |||
|             "versionInfo": "v{version} - generated on {date}" | ||||
|         }, | ||||
|         "pickLanguage": "Select language", | ||||
|         "poweredByMapComplete": "Powered by MapComplete - crowdsourced, thematic maps with OpenStreetMap", | ||||
|         "poweredByOsm": "Powered by OpenStreetMap", | ||||
|         "questionBox": { | ||||
|             "answeredMultiple": "You answered {answered} questions", | ||||
|  | @ -394,6 +395,7 @@ | |||
|             "searching": "Searching…" | ||||
|         }, | ||||
|         "searchAnswer": "Search an option…", | ||||
|         "seeIndex": "See the overview with all thematic maps", | ||||
|         "share": "Share", | ||||
|         "sharescreen": { | ||||
|             "copiedToClipboard": "Link copied to clipboard", | ||||
|  |  | |||
|  | @ -10754,6 +10754,26 @@ | |||
|                 }, | ||||
|                 "question": "What accessibility features should be applied?" | ||||
|             }, | ||||
|             "add-new-feature": { | ||||
|                 "mappings": { | ||||
|                     "0": { | ||||
|                         "then": "Adding a new feature is done with the button at the bottom left. Clicking the map does nothing" | ||||
|                     }, | ||||
|                     "1": { | ||||
|                         "then": "When clicking or tapping the map, a marker pops up where a new feature is added" | ||||
|                     }, | ||||
|                     "2": { | ||||
|                         "then": "When right-clicking or long-pressing the map, a marker pops up where a new feature can be added" | ||||
|                     }, | ||||
|                     "3": { | ||||
|                         "then": "When clicking or tapping the map, a marker pops up where a new feature can be added. Additionally, a button at the bottom left is shown" | ||||
|                     }, | ||||
|                     "4": { | ||||
|                         "then": "When right-clicking or long-pressing the map, a marker pops up where a new feature can be added. Additionally, a button at the bottom left is shown" | ||||
|                     } | ||||
|                 }, | ||||
|                 "question": "How should the menu to add a new feature be opened?" | ||||
|             }, | ||||
|             "all-questions-at-once": { | ||||
|                 "mappings": { | ||||
|                     "0": { | ||||
|  |  | |||
|  | @ -6,22 +6,26 @@ import { UIEventSource } from "../UIEventSource" | |||
| import { QueryParameters } from "../Web/QueryParameters" | ||||
| import Constants from "../../Models/Constants" | ||||
| import { Utils } from "../../Utils" | ||||
| import { Query } from "pg" | ||||
| 
 | ||||
| class FeatureSwitchUtils { | ||||
|     /** Helper function to initialize feature switches | ||||
|      * | ||||
|      */ | ||||
|     static initSwitch(key: string, deflt: boolean, documentation: string): UIEventSource<boolean> { | ||||
|         const defaultValue = deflt | ||||
|         const queryParam = QueryParameters.GetQueryParameter( | ||||
|             key, | ||||
|             "" + defaultValue, | ||||
|             documentation, | ||||
|             { stackOffset: -1 } | ||||
|             { stackOffset: -1 }, | ||||
|         ) | ||||
| 
 | ||||
|         // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened
 | ||||
|         return queryParam.sync( | ||||
|             (str) => (str === undefined ? defaultValue : str !== "false"), | ||||
|             [], | ||||
|             (b) => (b == defaultValue ? undefined : "" + b) | ||||
|             (b) => (b == defaultValue ? undefined : "" + b), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | @ -33,7 +37,7 @@ export class OsmConnectionFeatureSwitches { | |||
|         this.featureSwitchFakeUser = QueryParameters.GetBooleanQueryParameter( | ||||
|             "fake-user", | ||||
|             false, | ||||
|             "If true, 'dryrun' mode is activated and a fake user account is loaded" | ||||
|             "If true, 'dryrun' mode is activated and a fake user account is loaded", | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | @ -69,19 +73,41 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches { | |||
|         super() | ||||
|         this.layoutToUse = layoutToUse | ||||
| 
 | ||||
|         // Helper function to initialize feature switches
 | ||||
| 
 | ||||
|         const legacyRewrite: Record<string, string | string[]> = { | ||||
|             "fs-userbadge": "fs-enable-login", | ||||
|             "fs-layers": ["fs-filter", "fs-background"], | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         for (const key in legacyRewrite) { | ||||
|             let intoList = legacyRewrite[key] | ||||
|             if (!QueryParameters.wasInitialized(key)) { | ||||
|                 continue | ||||
|             } | ||||
|             if (typeof intoList === "string") { | ||||
|                 intoList = [intoList] | ||||
|             } | ||||
|             for (const into of intoList) { | ||||
|                 if (!QueryParameters.wasInitialized(into)) { | ||||
|                     const v = QueryParameters.GetQueryParameter(key, "", "").data | ||||
|                     console.log("Adding url param due to legacy:", key, "-->", into, "(", v + ")") | ||||
|                     QueryParameters.GetQueryParameter(into, "", "").setData(v) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-enable-login", | ||||
|             layoutToUse?.enableUserBadge ?? true, | ||||
|             "Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode." | ||||
|             "Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode.", | ||||
|         ) | ||||
|         { | ||||
|             if (QueryParameters.wasInitialized("fs-userbadge")) { | ||||
|                 // userbadge is the legacy name for 'enable-login'
 | ||||
|                 this.featureSwitchEnableLogin.setData( | ||||
|                     QueryParameters.GetBooleanQueryParameter("fs-userbadge", undefined, "Legacy") | ||||
|                         .data | ||||
|                         .data, | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | @ -89,60 +115,60 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches { | |||
|         this.featureSwitchSearch = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-search", | ||||
|             layoutToUse?.enableSearch ?? true, | ||||
|             "Disables/Enables the search bar" | ||||
|             "Disables/Enables the search bar", | ||||
|         ) | ||||
|         this.featureSwitchBackgroundSelection = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-background", | ||||
|             layoutToUse?.enableBackgroundLayerSelection ?? true, | ||||
|             "Disables/Enables the background layer control" | ||||
|             "Disables/Enables the background layer control where a user can enable e.g. aerial imagery", | ||||
|         ) | ||||
| 
 | ||||
|         this.featureSwitchFilter = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-filter", | ||||
|             layoutToUse?.enableLayers ?? true, | ||||
|             "Disables/Enables the filter view" | ||||
|             "Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties", | ||||
|         ) | ||||
| 
 | ||||
|         this.featureSwitchWelcomeMessage = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-welcome-message", | ||||
|             true, | ||||
|             "Disables/enables the help menu or welcome message" | ||||
|             "Disables/enables the help menu or welcome message", | ||||
|         ) | ||||
|         this.featureSwitchCommunityIndex = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-community-index", | ||||
|             this.featureSwitchEnableLogin.data, | ||||
|             "Disables/enables the button to get in touch with the community" | ||||
|             "Disables/enables the button to get in touch with the community", | ||||
|         ) | ||||
|         this.featureSwitchExtraLinkEnabled = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-iframe-popout", | ||||
|             true, | ||||
|             "Disables/Enables the extraLink button. By default, if in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch or another extraLink button is enabled)" | ||||
|             "Disables/Enables the extraLink button. By default, if in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch or another extraLink button is enabled)", | ||||
|         ) | ||||
|         this.featureSwitchBackToThemeOverview = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-homepage-link", | ||||
|             layoutToUse?.enableMoreQuests ?? true, | ||||
|             "Disables/Enables the various links which go back to the index page with the theme overview" | ||||
|             "Disables/Enables the various links which go back to the index page with the theme overview", | ||||
|         ) | ||||
|         this.featureSwitchShareScreen = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-share-screen", | ||||
|             layoutToUse?.enableShareScreen ?? true, | ||||
|             "Disables/Enables the 'Share-screen'-tab in the welcome message" | ||||
|             "Disables/Enables the 'Share-screen'-tab in the welcome message", | ||||
|         ) | ||||
|         this.featureSwitchGeolocation = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-geolocation", | ||||
|             layoutToUse?.enableGeolocation ?? true, | ||||
|             "Disables/Enables the geolocation button" | ||||
|             "Disables/Enables the geolocation button", | ||||
|         ) | ||||
|         this.featureSwitchShowAllQuestions = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-all-questions", | ||||
|             layoutToUse?.enableShowAllQuestions ?? false, | ||||
|             "Always show all questions" | ||||
|             "Always show all questions", | ||||
|         ) | ||||
| 
 | ||||
|         this.featureSwitchEnableExport = FeatureSwitchUtils.initSwitch( | ||||
|             "fs-export", | ||||
|             layoutToUse?.enableExportButton ?? true, | ||||
|             "Enable the export as GeoJSON and CSV button" | ||||
|             "Enable the export as GeoJSON and CSV button", | ||||
|         ) | ||||
| 
 | ||||
|         let testingDefaultValue = false | ||||
|  | @ -156,59 +182,59 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches { | |||
|         this.featureSwitchIsTesting = QueryParameters.GetBooleanQueryParameter( | ||||
|             "test", | ||||
|             testingDefaultValue, | ||||
|             "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" | ||||
|             "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", | ||||
|         ) | ||||
| 
 | ||||
|         this.featureSwitchIsDebugging = QueryParameters.GetBooleanQueryParameter( | ||||
|             "debug", | ||||
|             false, | ||||
|             "If true, shows some extra debugging help such as all the available tags on every object" | ||||
|             "If true, shows some extra debugging help such as all the available tags on every object", | ||||
|         ) | ||||
| 
 | ||||
|         this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter( | ||||
|             "moreprivacy", | ||||
|             layoutToUse.enableMorePrivacy, | ||||
|             "If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken." | ||||
|             "If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken.", | ||||
|         ) | ||||
| 
 | ||||
|         this.overpassUrl = QueryParameters.GetQueryParameter( | ||||
|             "overpassUrl", | ||||
|             (layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","), | ||||
|             "Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter" | ||||
|             "Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter", | ||||
|         ).sync( | ||||
|             (param) => param?.split(","), | ||||
|             [], | ||||
|             (urls) => urls?.join(",") | ||||
|             (urls) => urls?.join(","), | ||||
|         ) | ||||
| 
 | ||||
|         this.overpassTimeout = UIEventSource.asInt( | ||||
|             QueryParameters.GetQueryParameter( | ||||
|                 "overpassTimeout", | ||||
|                 "" + layoutToUse?.overpassTimeout, | ||||
|                 "Set a different timeout (in seconds) for queries in overpass" | ||||
|             ) | ||||
|                 "Set a different timeout (in seconds) for queries in overpass", | ||||
|             ), | ||||
|         ) | ||||
| 
 | ||||
|         this.overpassMaxZoom = UIEventSource.asFloat( | ||||
|             QueryParameters.GetQueryParameter( | ||||
|                 "overpassMaxZoom", | ||||
|                 "" + layoutToUse?.overpassMaxZoom, | ||||
|                 " point to switch between OSM-api and overpass" | ||||
|             ) | ||||
|                 " point to switch between OSM-api and overpass", | ||||
|             ), | ||||
|         ) | ||||
| 
 | ||||
|         this.osmApiTileSize = UIEventSource.asInt( | ||||
|             QueryParameters.GetQueryParameter( | ||||
|                 "osmApiTileSize", | ||||
|                 "" + layoutToUse?.osmApiTileSize, | ||||
|                 "Tilesize when the OSM-API is used to fetch data within a BBOX" | ||||
|             ) | ||||
|                 "Tilesize when the OSM-API is used to fetch data within a BBOX", | ||||
|             ), | ||||
|         ) | ||||
| 
 | ||||
|         this.backgroundLayerId = QueryParameters.GetQueryParameter( | ||||
|             "background", | ||||
|             layoutToUse?.defaultBackgroundId, | ||||
|             "The id of the background layer to start with" | ||||
|             "The id of the background layer to start with", | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -297,6 +297,7 @@ export default class UserRelatedState { | |||
|             _applicationOpened: new Date().toISOString(), | ||||
|             _supports_sharing: | ||||
|                 typeof window === "undefined" ? "no" : window.navigator.share ? "yes" : "no", | ||||
|             _iframe: Utils.isIframe ? "yes" : "no" | ||||
|         }) | ||||
| 
 | ||||
|         for (const key in Constants.userJourney) { | ||||
|  |  | |||
|  | @ -40,8 +40,8 @@ export default class CopyrightPanel extends Combine { | |||
|         const t = Translations.t.general.attribution | ||||
|         const layoutToUse = state.layout | ||||
| 
 | ||||
|         const iconAttributions: BaseUIElement[] = layoutToUse | ||||
|             .getUsedImages() | ||||
|         const iconAttributions: BaseUIElement[] = Utils.Dedup(layoutToUse | ||||
|             .getUsedImages()) | ||||
|             .map(CopyrightPanel.IconAttribution) | ||||
| 
 | ||||
|         let maintainer: BaseUIElement = undefined | ||||
|  |  | |||
|  | @ -11,10 +11,9 @@ | |||
|   export let state: SpecialVisualizationState | ||||
|   let theme = state.layout?.id ?? "" | ||||
|   let config: ExtraLinkConfig = state.layout.extraLink | ||||
|   const isIframe = window !== window.top | ||||
|   let basepath = window.location.host | ||||
|   let showWelcomeMessageSwitch = state.featureSwitches.featureSwitchWelcomeMessage | ||||
| 
 | ||||
|   const isIframe = Utils.isIframe | ||||
|   const t = Translations.t.general | ||||
|   const href = state.mapProperties.location.map( | ||||
|     (loc) => { | ||||
|  | @ -36,7 +35,7 @@ | |||
|       href={$href} | ||||
|       target={config.newTab ? "_blank" : ""} | ||||
|       rel="noopener" | ||||
|       class="pointer-events-auto flex rounded-full border-black" | ||||
|       class="button pointer-events-auto flex rounded-full border-black" | ||||
|     > | ||||
|       <Icon icon={config.icon} clss="w-6 h-6 m-2" /> | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
|   import Location_refused from "../../assets/svg/Location_refused.svelte" | ||||
|   import Location from "../../assets/svg/Location.svelte" | ||||
|   import ChevronDoubleLeft from "@babeard/svelte-heroicons/mini/ChevronDoubleLeft" | ||||
|   import Constants from "../../Models/Constants" | ||||
| 
 | ||||
|   /** | ||||
|    * The theme introduction panel | ||||
|  | @ -149,13 +150,20 @@ | |||
|     {/if} | ||||
|   </div> | ||||
| 
 | ||||
|   {#if Utils.isIframe} | ||||
|     <div  class="flex justify-end link-underline"> | ||||
|     <a href="https://mapcomplete.org" target="_blank"> | ||||
|       <Tr t={Translations.t.general.poweredByMapComplete}/> | ||||
|     </a> | ||||
|     </div> | ||||
|   {:else} | ||||
|     <If condition={state.featureSwitches.featureSwitchBackToThemeOverview}> | ||||
|       <div class="link-underline m-2 mx-4 flex w-full"> | ||||
|       <!-- bottom buttons, a bit hidden away: switch layout --> | ||||
|         <a class="flex w-fit items-center justify-end" href={Utils.HomepageLink()}> | ||||
|           <ChevronDoubleLeft class="h-4 w-4" /> | ||||
|           <Tr t={Translations.t.general.backToIndex} /> | ||||
|         </a> | ||||
|       </div> | ||||
|     </If> | ||||
|   {/if} | ||||
| </div> | ||||
|  |  | |||
|  | @ -42,9 +42,10 @@ | |||
|   let knownImages = comparisonState.bindD((ct) => ct.knownImages) | ||||
|   let propertyKeysExternal = comparisonState.mapD((ct) => ct.propertyKeysExternal) | ||||
|   let hasDifferencesAtStart = comparisonState.mapD((ct) => ct.hasDifferencesAtStart) | ||||
|   let enableLogin=  state.featureSwitches.featureSwitchEnableLogin | ||||
| </script> | ||||
| 
 | ||||
| {#if !$sourceUrl} | ||||
| {#if !$sourceUrl || !$enableLogin} | ||||
|   <!-- empty block --> | ||||
| {:else if $externalData === undefined} | ||||
|   <Loading /> | ||||
|  |  | |||
|  | @ -54,10 +54,9 @@ export class ImageCarousel extends Toggle { | |||
|         ) | ||||
| 
 | ||||
|         super( | ||||
|             new SlideShow(uiElements).SetClass("w-full"), | ||||
|             new SlideShow(uiElements).SetClass("w-full block w-full my-4"), | ||||
|             undefined, | ||||
|             uiElements.map((els) => els.length > 0) | ||||
|         ) | ||||
|         this.SetClass("block w-full") | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -25,11 +25,14 @@ | |||
|   const t = Translations.t.image.nearby | ||||
| 
 | ||||
|   let expanded = false | ||||
|   let enableLogin = state.featureSwitches.featureSwitchEnableLogin | ||||
| </script> | ||||
| 
 | ||||
| {#if enableLogin.data} | ||||
| <AccordionSingle> | ||||
|   <span slot="header" class="p-2 text-base"> | ||||
|     <Tr t={t.seeNearby} /> | ||||
|   </span> | ||||
|   <NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer} /> | ||||
| </AccordionSingle> | ||||
|   {/if} | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ | |||
|   <LoginButton clss="small w-full" osmConnection={state.osmConnection} slot="not-logged-in"> | ||||
|     <Tr t={Translations.t.image.pleaseLogin} /> | ||||
|   </LoginButton> | ||||
|   <div class="flex flex-col"> | ||||
|   <div class="flex flex-col my-4"> | ||||
|     <UploadingImageCounter {state} {tags} /> | ||||
|     {#each $errors as error} | ||||
|       <Tr t={error} cls="alert" /> | ||||
|  |  | |||
|  | @ -932,9 +932,11 @@ export class ToTextualDescription { | |||
|     public static createTextualDescriptionFor( | ||||
|         oh: opening_hours, | ||||
|         ranges: OpeningRange[][] | ||||
|     ): Translation { | ||||
|     ): Translation | undefined { | ||||
|         const t = Translations.t.general.opening_hours | ||||
| 
 | ||||
|         if(!ranges){ | ||||
|             return undefined | ||||
|         } | ||||
|         if (!ranges?.some((r) => r.length > 0)) { | ||||
|             // <!-- No changes to the opening hours in the next week; probably open 24/7, permanently closed, opening far in the future or unkown -->
 | ||||
|             if (oh.getNextChange() === undefined) { | ||||
|  | @ -1029,9 +1031,9 @@ export class ToTextualDescription { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private static createRangesFor(ranges: OpeningRange[]): Translation { | ||||
|     private static createRangesFor(ranges: OpeningRange[]): Translation | undefined { | ||||
|         if (ranges.length === 0) { | ||||
|             //    return undefined
 | ||||
|             return undefined | ||||
|         } | ||||
|         let tr = ToTextualDescription.createRangeFor(ranges[0]) | ||||
|         for (let i = 1; i < ranges.length; i++) { | ||||
|  |  | |||
|  | @ -26,7 +26,6 @@ export default class OpeningHoursVisualization extends Toggle { | |||
| 
 | ||||
|     constructor( | ||||
|         tags: UIEventSource<Record<string, string>>, | ||||
|         state: { osmConnection?: OsmConnection }, | ||||
|         key: string, | ||||
|         prefix = "", | ||||
|         postfix = "" | ||||
|  | @ -56,7 +55,7 @@ export default class OpeningHoursVisualization extends Toggle { | |||
|                 ) | ||||
|                 Locale.language.mapD((lng) => { | ||||
|                     console.debug("Setting OH description to", lng, textual) | ||||
|                     vis.ConstructElement().ariaLabel = textual.textFor(lng) | ||||
|                     vis.ConstructElement().ariaLabel = textual?.textFor(lng) | ||||
|                 }) | ||||
|                 return vis | ||||
|             }) | ||||
|  | @ -75,18 +74,14 @@ export default class OpeningHoursVisualization extends Toggle { | |||
|         ranges: OpeningRange[][], | ||||
|         lastMonday: Date | ||||
|     ): BaseUIElement { | ||||
|         /* First, a small sanity check. The business might be permanently closed, 24/7 opened or something other special | ||||
|          * So, we have to handle the case that ranges is completely empty*/ | ||||
|         if (ranges.filter((range) => range.length > 0).length === 0) { | ||||
|             return OpeningHoursVisualization.ShowSpecialCase(oh).SetClass( | ||||
|                 "p-4 rounded-full block bg-gray-200" | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         /** With all the edge cases handled, we can actually construct the table! **/ | ||||
| 
 | ||||
|         // First, a small sanity check. The business might be permanently closed, 24/7 opened or something other special
 | ||||
|         if (ranges.some((range) => range.length > 0)) { | ||||
|             // The normal case: we have items for the coming days
 | ||||
|             return OpeningHoursVisualization.ConstructVizTable(oh, ranges, lastMonday) | ||||
|         } | ||||
|          // The special case that range is completely empty
 | ||||
|         return OpeningHoursVisualization.ShowSpecialCase(oh) | ||||
|     } | ||||
| 
 | ||||
|     private static ConstructVizTable( | ||||
|         oh: any, | ||||
|  | @ -308,6 +303,6 @@ export default class OpeningHoursVisualization extends Toggle { | |||
|             opensAtDate.getHours(), | ||||
|             opensAtDate.getMinutes() | ||||
|         )}` | ||||
|         return Translations.t.general.opening_hours.closed_until.Subs({ date: willOpenAt }) | ||||
|         return Translations.t.general.opening_hours.closed_until.Subs({ date: opensAtDate.toLocaleString() }) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -94,6 +94,8 @@ | |||
|   let answered: number = 0 | ||||
|   let skipped: number = 0 | ||||
| 
 | ||||
|   let loginEnabled = state.featureSwitches.featureSwitchEnableLogin | ||||
| 
 | ||||
|   function skip(question: { id: string }, didAnswer: boolean = false) { | ||||
|     skippedQuestions.data.add(question.id) // Must use ID, the config object might be a copy of the original | ||||
|     skippedQuestions.ping() | ||||
|  | @ -108,6 +110,7 @@ | |||
|   } | ||||
| </script> | ||||
| 
 | ||||
| {#if $loginEnabled} | ||||
| <div | ||||
|   bind:this={questionboxElem} | ||||
|   aria-live="polite" | ||||
|  | @ -197,3 +200,4 @@ | |||
|     </div> | ||||
|   {/if} | ||||
| </div> | ||||
|   {/if} | ||||
|  |  | |||
|  | @ -83,7 +83,7 @@ | |||
|       {config.id} | ||||
|     </span> | ||||
|   {/if} | ||||
|   {#if config.question && (!editingEnabled || $editingEnabled)} | ||||
|   {#if config.question} | ||||
|     {#if editMode} | ||||
|       <TagRenderingQuestion | ||||
|         {config} | ||||
|  | @ -106,7 +106,8 @@ | |||
|       </TagRenderingQuestion> | ||||
|     {:else} | ||||
|       <div class="low-interaction flex items-center justify-between overflow-hidden rounded pl-2"> | ||||
|         <TagRenderingAnswer id={answerId} {config} {tags} {selectedElement} {state} {layer} /> | ||||
|         <TagRenderingAnswer id={answerId} {config} {tags} {selectedElement} {state} {layer} extraClasses="my-2"/> | ||||
|         {#if (!editingEnabled || $editingEnabled)} | ||||
|           <EditButton | ||||
|             arialabel={config.editButtonAriaLabel} | ||||
|             ariaLabelledBy={answerId} | ||||
|  | @ -114,11 +115,10 @@ | |||
|               editMode = true | ||||
|             }} | ||||
|           /> | ||||
|         {/if} | ||||
|       </div> | ||||
|     {/if} | ||||
|   {:else} | ||||
|     <div class="h-full w-full overflow-auto"> | ||||
|     <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> | ||||
|     </div> | ||||
|   {/if} | ||||
| </div> | ||||
|  |  | |||
|  | @ -794,7 +794,7 @@ export default class SpecialVisualizations { | |||
|                     "A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`", | ||||
|                 constr: (state, tagSource: UIEventSource<any>, args) => { | ||||
|                     const [key, prefix, postfix] = args | ||||
|                     return new OpeningHoursVisualization(tagSource, state, key, prefix, postfix) | ||||
|                     return new OpeningHoursVisualization(tagSource, key, prefix, postfix) | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|  |  | |||
|  | @ -1,4 +1,7 @@ | |||
| <script lang="ts"> | ||||
| import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization" | ||||
| </script> | ||||
| 
 | ||||
| <main /> | ||||
| <main > | ||||
|   <ToSvelte construct={new OpeningHoursVisualization()} | ||||
| </main> | ||||
|  |  | |||
|  | @ -124,11 +124,11 @@ | |||
|   state.mapProperties.installCustomKeyboardHandler(viewport) | ||||
|   let canZoomIn = mapproperties.maxzoom.map( | ||||
|     (mz) => mapproperties.zoom.data < mz, | ||||
|     [mapproperties.zoom] | ||||
|     [mapproperties.zoom], | ||||
|   ) | ||||
|   let canZoomOut = mapproperties.minzoom.map( | ||||
|     (mz) => mapproperties.zoom.data > mz, | ||||
|     [mapproperties.zoom] | ||||
|     [mapproperties.zoom], | ||||
|   ) | ||||
| 
 | ||||
|   function updateViewport() { | ||||
|  | @ -165,7 +165,7 @@ | |||
|   onDestroy( | ||||
|     rasterLayer.addCallbackAndRunD((l) => { | ||||
|       rasterLayerName = l.properties.name | ||||
|     }) | ||||
|     }), | ||||
|   ) | ||||
|   let previewedImage = state.previewedImage | ||||
| 
 | ||||
|  | @ -196,7 +196,7 @@ | |||
|   let openMapButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
|   let openMenuButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
|   let openCurrentViewLayerButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>( | ||||
|     undefined | ||||
|     undefined, | ||||
|   ) | ||||
|   let _openNewElementButton: HTMLButtonElement | ||||
|   let openNewElementButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
|  | @ -478,7 +478,7 @@ | |||
|     <FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}> | ||||
|       <span slot="close-button"><!-- Disable the close button --></span> | ||||
|       <TabbedGroup | ||||
|         condition1={state.featureSwitches.featureSwitchFilter} | ||||
|         condition1={state.featureSwitches.featureSwitchEnableExport} | ||||
|         tab={state.guistate.themeViewTabIndex} | ||||
|       > | ||||
|         <div slot="post-tablist"> | ||||
|  | @ -572,9 +572,14 @@ | |||
|         <div class="link-underline links-w-full m-2 flex flex-col gap-y-1" slot="content0"> | ||||
|           <Tr t={Translations.t.general.aboutMapComplete.intro} /> | ||||
| 
 | ||||
| 
 | ||||
|           <a class="flex" href={Utils.HomepageLink()}> | ||||
|             <Add class="h-6 w-6" /> | ||||
|             {#if Utils.isIframe} | ||||
|               <Tr t={Translations.t.general.seeIndex} /> | ||||
|             {:else} | ||||
|               <Tr t={Translations.t.general.backToIndex} /> | ||||
|             {/if} | ||||
|           </a> | ||||
| 
 | ||||
|           <a class="flex" href="https://github.com/pietervdvn/MapComplete/" target="_blank"> | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import colors from "./assets/colors.json" | ||||
| import DOMPurify from "dompurify" | ||||
| 
 | ||||
| export class Utils { | ||||
|  | @ -8,7 +7,6 @@ export class Utils { | |||
|      * This is a workaround and yet another hack | ||||
|      */ | ||||
|     public static runningFromConsole = typeof window === "undefined" | ||||
|     public static readonly assets_path = "./assets/svg/" | ||||
|     public static externalDownloadFunction: ( | ||||
|         url: string, | ||||
|         headers?: any | ||||
|  | @ -146,6 +144,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | |||
|         } | ||||
|     >() | ||||
| 
 | ||||
|     public static readonly isIframe = !Utils.runningFromConsole && window !== window.top | ||||
| 
 | ||||
| 
 | ||||
|     public static initDomPurify() { | ||||
|         if (Utils.runningFromConsole) { | ||||
|             return | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue