forked from MapComplete/MapComplete
		
	Add minimap baseElement; add this as special rendering, add minimap beneath direction input element
This commit is contained in:
		
							parent
							
								
									89df28ae06
								
							
						
					
					
						commit
						eba1772ab9
					
				
					 16 changed files with 411 additions and 153 deletions
				
			
		|  | @ -352,6 +352,11 @@ export class InitUiElements { | |||
| 
 | ||||
|         State.state.backgroundLayer = State.state.backgroundLayerId | ||||
|             .map((selectedId: string) => { | ||||
|                 if(selectedId === undefined){ | ||||
|                     return AvailableBaseLayers.osmCarto | ||||
|                 } | ||||
|                  | ||||
|                  | ||||
|                 const available = State.state.availableBackgroundLayers.data; | ||||
|                 for (const layer of available) { | ||||
|                     if (layer.id === selectedId) { | ||||
|  |  | |||
|  | @ -14,18 +14,19 @@ import {Utils} from "../../Utils"; | |||
| export default class AvailableBaseLayers { | ||||
| 
 | ||||
| 
 | ||||
|     public static osmCarto: BaseLayer = | ||||
|     public static  osmCarto: BaseLayer =  | ||||
|         { | ||||
|             id: "osm", | ||||
|             name: "OpenStreetMap", | ||||
|             layer: AvailableBaseLayers.CreateBackgroundLayer("osm", "OpenStreetMap", | ||||
|                 "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "OpenStreetMap", "https://openStreetMap.org/copyright", | ||||
|                 19, | ||||
|                 false, false), | ||||
|                 name: "OpenStreetMap", | ||||
|             layer: () => AvailableBaseLayers.CreateBackgroundLayer("osm", "OpenStreetMap", | ||||
|             "https://tile.openstreetmap.org/{z}/{x}/{y}.png", "OpenStreetMap", "https://openStreetMap.org/copyright", | ||||
|             19, | ||||
|             false, false), | ||||
|             feature: null, | ||||
|             max_zoom: 19, | ||||
|             min_zoom: 0 | ||||
|         } | ||||
|     } | ||||
|         | ||||
| 
 | ||||
| 
 | ||||
|     public static layerOverview = AvailableBaseLayers.LoadRasterIndex().concat(AvailableBaseLayers.LoadProviderIndex()); | ||||
|  | @ -123,7 +124,7 @@ export default class AvailableBaseLayers { | |||
|                 continue | ||||
|             } | ||||
| 
 | ||||
|             const leafletLayer = AvailableBaseLayers.CreateBackgroundLayer( | ||||
|             const leafletLayer: () => TileLayer = () => AvailableBaseLayers.CreateBackgroundLayer( | ||||
|                 props.id, | ||||
|                 props.name, | ||||
|                 props.url, | ||||
|  | @ -150,10 +151,10 @@ export default class AvailableBaseLayers { | |||
|     private static LoadProviderIndex(): BaseLayer[] { | ||||
|         // @ts-ignore
 | ||||
|         X; // Import X to make sure the namespace is not optimized away
 | ||||
|         function l(id: string, name: string) { | ||||
|         function l(id: string, name: string) : BaseLayer{ | ||||
|             try { | ||||
|                 const layer: any = L.tileLayer.provider(id, undefined); | ||||
|                 return { | ||||
|                 const layer: any = () => L.tileLayer.provider(id, undefined); | ||||
|                 const baseLayer : BaseLayer =  { | ||||
|                     feature: null, | ||||
|                     id: id, | ||||
|                     name: name, | ||||
|  | @ -161,6 +162,7 @@ export default class AvailableBaseLayers { | |||
|                     min_zoom: layer.minzoom, | ||||
|                     max_zoom: layer.maxzoom | ||||
|                 } | ||||
|                 return baseLayer | ||||
|             } catch (e) { | ||||
|                 console.error("Could not find provided layer", name, e); | ||||
|                 return null; | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import {TileLayer} from "leaflet"; | |||
| export default interface BaseLayer { | ||||
|     id: string, | ||||
|     name: string, | ||||
|     layer: TileLayer, | ||||
|     layer: () => TileLayer, | ||||
|     max_zoom: number, | ||||
|     min_zoom: number; | ||||
|     feature: any, | ||||
|  |  | |||
							
								
								
									
										19
									
								
								Svg.ts
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								Svg.ts
									
										
									
									
									
								
							|  | @ -104,11 +104,26 @@ export default class Svg { | |||
|     public static direction_svg() { return new Img(Svg.direction, true);} | ||||
|     public static direction_ui() { return new FixedUiElement(Svg.direction_img);} | ||||
| 
 | ||||
|     public static direction_gradient = " <svg    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"    xmlns:cc=\"http://creativecommons.org/ns#\"    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"    xmlns:svg=\"http://www.w3.org/2000/svg\"    xmlns=\"http://www.w3.org/2000/svg\"    xmlns:xlink=\"http://www.w3.org/1999/xlink\"    id=\"svg8\"    version=\"1.1\"    viewBox=\"0 0 100 100\"    height=\"100\"    width=\"100\">   <metadata      id=\"metadata8\">     <rdf:RDF>       <cc:Work          rdf:about=\"\">         <dc:format>image/svg+xml</dc:format>         <dc:type            rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />       </cc:Work>     </rdf:RDF>   </metadata>   <defs      id=\"defs6\">     <linearGradient        id=\"linearGradient820\">       <stop          style=\"stop-color:#000000;stop-opacity:1;\"          offset=\"0\"          id=\"innercolor\" />       <stop          style=\"stop-color:#000000;stop-opacity:0\"          offset=\"1\"          id=\"outercolor\" />     </linearGradient>     <radialGradient        xlink:href=\"#linearGradient820\"        id=\"radialGradient828\"        cx=\"49.787739\"        cy=\"53.828533\"        fx=\"49.787739\"        fy=\"53.828533\"        r=\"28.883806\"        gradientTransform=\"matrix(1.5439431,-0.01852438,0.02075364,1.7297431,-27.986574,-42.187244)\"        gradientUnits=\"userSpaceOnUse\" />   </defs>   <path      id=\"path821\"      d=\"M 50,50 21.042889,9.3993342 C 36.191421,-2.001434 60.726552,-3.6768009 78.8105,9.1490935 Z\"      style=\"fill:url(#radialGradient828);fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" /> </svg> " | ||||
|     public static direction_gradient = " <svg    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"    xmlns:cc=\"http://creativecommons.org/ns#\"    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"    xmlns:svg=\"http://www.w3.org/2000/svg\"    xmlns=\"http://www.w3.org/2000/svg\"    xmlns:xlink=\"http://www.w3.org/1999/xlink\"    xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"    xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"    version=\"1.0\"    width=\"860.50732pt\"    height=\"860.50732pt\"    viewBox=\"0 0 860.50732 860.50732\"    preserveAspectRatio=\"xMidYMid meet\"    id=\"svg14\"    sodipodi:docname=\"direction_gradient.svg\"    inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\">   <defs      id=\"defs18\">     <linearGradient        inkscape:collect=\"always\"        id=\"linearGradient832\">       <stop          style=\"stop-color:#000000;stop-opacity:1;\"          offset=\"0\"          id=\"stop828\" />       <stop          style=\"stop-color:#000000;stop-opacity:0;\"          offset=\"1\"          id=\"stop830\" />     </linearGradient>     <radialGradient        inkscape:collect=\"always\"        xlink:href=\"#linearGradient832\"        id=\"radialGradient838\"        cx=\"430.25363\"        cy=\"519.61188\"        fx=\"430.25363\"        fy=\"519.61188\"        r=\"305.54589\"        gradientTransform=\"matrix(0.95288409,-0.94890664,0.94542304,0.94938587,-470.98122,345.21193)\"        gradientUnits=\"userSpaceOnUse\" />   </defs>   <sodipodi:namedview      pagecolor=\"#ffffff\"      bordercolor=\"#666666\"      borderopacity=\"1\"      objecttolerance=\"10\"      gridtolerance=\"10\"      guidetolerance=\"10\"      inkscape:pageopacity=\"0\"      inkscape:pageshadow=\"2\"      inkscape:window-width=\"1920\"      inkscape:window-height=\"999\"      id=\"namedview16\"      showgrid=\"false\"      showguides=\"true\"      inkscape:guide-bbox=\"true\"      inkscape:zoom=\"0.70710678\"      inkscape:cx=\"279.00239\"      inkscape:cy=\"856.75313\"      inkscape:window-x=\"0\"      inkscape:window-y=\"0\"      inkscape:window-maximized=\"1\"      inkscape:current-layer=\"svg14\">     <sodipodi:guide        position=\"430.25363,862.49682\"        orientation=\"1,0\"        id=\"guide832\"        inkscape:locked=\"false\" />     <sodipodi:guide        position=\"-42.427977,430.25368\"        orientation=\"0,1\"        id=\"guide834\"        inkscape:locked=\"false\" />     <sodipodi:guide        position=\"398.27788,720.18823\"        orientation=\"0,1\"        id=\"guide840\"        inkscape:locked=\"false\" />   </sodipodi:namedview>   <metadata      id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF>   <cc:Work      rdf:about=\"\">     <dc:format>image/svg+xml</dc:format>     <dc:type        rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />     <dc:title />   </cc:Work> </rdf:RDF> </metadata>   <path      style=\"fill:url(#radialGradient838);fill-opacity:1;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\"      d=\"M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z\"      id=\"path836\"      inkscape:connector-curvature=\"0\"      sodipodi:nodetypes=\"ccccc\" /> </svg> " | ||||
|     public static direction_gradient_img = Img.AsImageElement(Svg.direction_gradient) | ||||
|     public static direction_gradient_svg() { return new Img(Svg.direction_gradient, true);} | ||||
|     public static direction_gradient_ui() { return new FixedUiElement(Svg.direction_gradient_img);} | ||||
| 
 | ||||
|     public static direction_masked = " <svg    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"    xmlns:cc=\"http://creativecommons.org/ns#\"    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"    xmlns:svg=\"http://www.w3.org/2000/svg\"    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\"    version=\"1.0\"    width=\"860.50732pt\"    height=\"860.50732pt\"    viewBox=\"0 0 860.50732 860.50732\"    preserveAspectRatio=\"xMidYMid meet\"    id=\"svg14\"    sodipodi:docname=\"direction_masked.svg\"    inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\">   <defs      id=\"defs18\" />   <sodipodi:namedview      pagecolor=\"#ffffff\"      bordercolor=\"#666666\"      borderopacity=\"1\"      objecttolerance=\"10\"      gridtolerance=\"10\"      guidetolerance=\"10\"      inkscape:pageopacity=\"0\"      inkscape:pageshadow=\"2\"      inkscape:window-width=\"1920\"      inkscape:window-height=\"999\"      id=\"namedview16\"      showgrid=\"false\"      showguides=\"true\"      inkscape:guide-bbox=\"true\"      inkscape:zoom=\"0.70710678\"      inkscape:cx=\"418.71218\"      inkscape:cy=\"725.43016\"      inkscape:window-x=\"0\"      inkscape:window-y=\"0\"      inkscape:window-maximized=\"1\"      inkscape:current-layer=\"svg14\">     <sodipodi:guide        position=\"430.25363,862.49682\"        orientation=\"1,0\"        id=\"guide832\"        inkscape:locked=\"false\" />     <sodipodi:guide        position=\"-42.427977,430.25368\"        orientation=\"0,1\"        id=\"guide834\"        inkscape:locked=\"false\" />   </sodipodi:namedview>   <metadata      id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF>   <cc:Work      rdf:about=\"\">     <dc:format>image/svg+xml</dc:format>     <dc:type        rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />   </cc:Work> </rdf:RDF> </metadata>   <path      style=\"fill:#000000;fill-opacity:0.39163497;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\"      d=\"M 124.70776,124.7084 A 432.375,432.375 0 0 0 -2.121338,430.25429 432.375,432.375 0 0 0 430.25366,862.62929 432.375,432.375 0 0 0 862.62866,430.25429 432.375,432.375 0 0 0 735.79811,124.70986 L 430.25366,430.25429 Z\"      id=\"path836\"      inkscape:connector-curvature=\"0\" /> </svg> " | ||||
|     public static direction_masked_img = Img.AsImageElement(Svg.direction_masked) | ||||
|     public static direction_masked_svg() { return new Img(Svg.direction_masked, true);} | ||||
|     public static direction_masked_ui() { return new FixedUiElement(Svg.direction_masked_img);} | ||||
| 
 | ||||
|     public static direction_outline = " <svg    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"    xmlns:cc=\"http://creativecommons.org/ns#\"    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"    xmlns:svg=\"http://www.w3.org/2000/svg\"    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\"    version=\"1.0\"    width=\"860.50732pt\"    height=\"860.50732pt\"    viewBox=\"0 0 860.50732 860.50732\"    preserveAspectRatio=\"xMidYMid meet\"    id=\"svg14\"    sodipodi:docname=\"direction_outline.svg\"    inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\">   <defs      id=\"defs18\" />   <sodipodi:namedview      pagecolor=\"#ffffff\"      bordercolor=\"#666666\"      borderopacity=\"1\"      objecttolerance=\"10\"      gridtolerance=\"10\"      guidetolerance=\"10\"      inkscape:pageopacity=\"0\"      inkscape:pageshadow=\"2\"      inkscape:window-width=\"1920\"      inkscape:window-height=\"999\"      id=\"namedview16\"      showgrid=\"false\"      showguides=\"true\"      inkscape:guide-bbox=\"true\"      inkscape:zoom=\"0.35355339\"      inkscape:cx=\"72.756921\"      inkscape:cy=\"792.67347\"      inkscape:window-x=\"0\"      inkscape:window-y=\"0\"      inkscape:window-maximized=\"1\"      inkscape:current-layer=\"svg14\">     <sodipodi:guide        position=\"430.25363,862.49682\"        orientation=\"1,0\"        id=\"guide832\"        inkscape:locked=\"false\" />     <sodipodi:guide        position=\"-42.427977,430.25368\"        orientation=\"0,1\"        id=\"guide834\"        inkscape:locked=\"false\" />   </sodipodi:namedview>   <metadata      id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF>   <cc:Work      rdf:about=\"\">     <dc:format>image/svg+xml</dc:format>     <dc:type        rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />     <dc:title></dc:title>   </cc:Work> </rdf:RDF> </metadata>   <path      style=\"fill:#000000;fill-opacity:0.39163501;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\"      d=\"M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z\"      id=\"path836\"      inkscape:connector-curvature=\"0\"      sodipodi:nodetypes=\"ccccc\" /> </svg> " | ||||
|     public static direction_outline_img = Img.AsImageElement(Svg.direction_outline) | ||||
|     public static direction_outline_svg() { return new Img(Svg.direction_outline, true);} | ||||
|     public static direction_outline_ui() { return new FixedUiElement(Svg.direction_outline_img);} | ||||
| 
 | ||||
|     public static direction_stroke = " <svg    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"    xmlns:cc=\"http://creativecommons.org/ns#\"    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"    xmlns:svg=\"http://www.w3.org/2000/svg\"    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\"    version=\"1.0\"    width=\"860.50732pt\"    height=\"860.50732pt\"    viewBox=\"0 0 860.50732 860.50732\"    preserveAspectRatio=\"xMidYMid meet\"    id=\"svg14\"    sodipodi:docname=\"direction_stroke.svg\"    inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\">   <defs      id=\"defs18\" />   <sodipodi:namedview      pagecolor=\"#ffffff\"      bordercolor=\"#666666\"      borderopacity=\"1\"      objecttolerance=\"10\"      gridtolerance=\"10\"      guidetolerance=\"10\"      inkscape:pageopacity=\"0\"      inkscape:pageshadow=\"2\"      inkscape:window-width=\"1920\"      inkscape:window-height=\"999\"      id=\"namedview16\"      showgrid=\"false\"      showguides=\"true\"      inkscape:guide-bbox=\"true\"      inkscape:zoom=\"1\"      inkscape:cx=\"482.69344\"      inkscape:cy=\"931.96415\"      inkscape:window-x=\"0\"      inkscape:window-y=\"0\"      inkscape:window-maximized=\"1\"      inkscape:current-layer=\"svg14\">     <sodipodi:guide        position=\"430.25363,862.49682\"        orientation=\"1,0\"        id=\"guide832\"        inkscape:locked=\"false\" />     <sodipodi:guide        position=\"-42.427977,430.25368\"        orientation=\"0,1\"        id=\"guide834\"        inkscape:locked=\"false\" />   </sodipodi:namedview>   <metadata      id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF>   <cc:Work      rdf:about=\"\">     <dc:format>image/svg+xml</dc:format>     <dc:type        rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />     <dc:title />   </cc:Work> </rdf:RDF> </metadata>   <path      style=\"fill: none !important;fill-opacity:0.39163501;stroke:#000000;stroke-width:3.74999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\"      d=\"M 735.79953,129.20772 C 654.7909,48.098612 544.88816,2.4790648 430.25363,2.3786263 315.61911,2.4790701 205.71637,48.098617 124.70775,129.20772 l 305.54588,305.54589 z\"      id=\"path836\"      inkscape:connector-curvature=\"0\"      sodipodi:nodetypes=\"ccccc\" /> </svg> " | ||||
|     public static direction_stroke_img = Img.AsImageElement(Svg.direction_stroke) | ||||
|     public static direction_stroke_svg() { return new Img(Svg.direction_stroke, true);} | ||||
|     public static direction_stroke_ui() { return new FixedUiElement(Svg.direction_stroke_img);} | ||||
| 
 | ||||
|     public static down = " <svg    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"    xmlns:cc=\"http://creativecommons.org/ns#\"    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"    xmlns:svg=\"http://www.w3.org/2000/svg\"    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\"    version=\"1.0\"    width=\"700\"    height=\"700\"    id=\"svg6\"    sodipodi:docname=\"down.svg\"    inkscape:version=\"0.92.4 (5da689c313, 2019-01-14)\">   <metadata      id=\"metadata12\">     <rdf:RDF>       <cc:Work          rdf:about=\"\">         <dc:format>image/svg+xml</dc:format>         <dc:type            rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />       </cc:Work>     </rdf:RDF>   </metadata>   <defs      id=\"defs10\" />   <sodipodi:namedview      pagecolor=\"#ffffff\"      bordercolor=\"#666666\"      borderopacity=\"1\"      objecttolerance=\"10\"      gridtolerance=\"10\"      guidetolerance=\"10\"      inkscape:pageopacity=\"0\"      inkscape:pageshadow=\"2\"      inkscape:window-width=\"1920\"      inkscape:window-height=\"1001\"      id=\"namedview8\"      showgrid=\"false\"      inkscape:zoom=\"0.33714286\"      inkscape:cx=\"477.91309\"      inkscape:cy=\"350\"      inkscape:window-x=\"0\"      inkscape:window-y=\"0\"      inkscape:window-maximized=\"1\"      inkscape:current-layer=\"svg6\" />   <g      transform=\"rotate(-180,342.1439,335.17672)\"      id=\"g4\">     <path        d=\"m -20,670.71582 c 0,-1.85843 349.99229,-699.98853 350.57213,-699.28671 1.94549,2.35478 350.06752,699.46087 349.427,699.71927 -0.41837,0.16878 -79.29725,-33.69092 -175.2864,-75.24377 l -174.52574,-75.55065 -174.2421,75.53732 c -95.83317,41.54551 -174.625237,75.5373 -175.093498,75.5373 -0.46826,0 -0.851382,-0.32075 -0.851382,-0.71276 z\"        style=\"fill:#00ff00;stroke:none\"        id=\"path2\"        inkscape:connector-curvature=\"0\" />   </g> </svg> " | ||||
|     public static down_img = Img.AsImageElement(Svg.down) | ||||
|     public static down_svg() { return new Img(Svg.down, true);} | ||||
|  | @ -319,4 +334,4 @@ export default class Svg { | |||
|     public static wikipedia_svg() { return new Img(Svg.wikipedia, true);} | ||||
|     public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);} | ||||
| 
 | ||||
| public static All = {"SocialImageForeground.svg": Svg.SocialImageForeground,"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"back.svg": Svg.back,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapcomplete_logo.svg": Svg.mapcomplete_logo,"mapillary.svg": Svg.mapillary,"mapillary_black.svg": Svg.mapillary_black,"min.svg": Svg.min,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-copyright.svg": Svg.osm_copyright,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"plus.svg": Svg.plus,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"star_half.svg": Svg.star_half,"star_outline.svg": Svg.star_outline,"star_outline_half.svg": Svg.star_outline_half,"statistics.svg": Svg.statistics,"translate.svg": Svg.translate,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};} | ||||
| public static All = {"SocialImageForeground.svg": Svg.SocialImageForeground,"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"back.svg": Svg.back,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"direction_masked.svg": Svg.direction_masked,"direction_outline.svg": Svg.direction_outline,"direction_stroke.svg": Svg.direction_stroke,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapcomplete_logo.svg": Svg.mapcomplete_logo,"mapillary.svg": Svg.mapillary,"mapillary_black.svg": Svg.mapillary_black,"min.svg": Svg.min,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-copyright.svg": Svg.osm_copyright,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"plus.svg": Svg.plus,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"star_half.svg": Svg.star_half,"star_outline.svg": Svg.star_outline,"star_outline_half.svg": Svg.star_outline_half,"statistics.svg": Svg.statistics,"translate.svg": Svg.translate,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};} | ||||
|  |  | |||
							
								
								
									
										144
									
								
								UI/Base/Minimap.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								UI/Base/Minimap.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,144 @@ | |||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import * as L from "leaflet"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| import BaseLayer from "../../Models/BaseLayer"; | ||||
| import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; | ||||
| import {Map} from "leaflet"; | ||||
| 
 | ||||
| export default class Minimap extends BaseUIElement { | ||||
| 
 | ||||
|     private static _nextId = 0; | ||||
|     public readonly leafletMap: UIEventSource<Map> = new UIEventSource<Map>(undefined) | ||||
|     private readonly _id: string; | ||||
|     private readonly _background: UIEventSource<BaseLayer>; | ||||
|     private readonly _location: UIEventSource<Loc>; | ||||
|     private _isInited = false; | ||||
|     private _allowMoving: boolean; | ||||
| 
 | ||||
|     constructor(options?: { | ||||
|                     background?: UIEventSource<BaseLayer>, | ||||
|                     location?: UIEventSource<Loc>, | ||||
|                     allowMoving?: boolean | ||||
|                 } | ||||
|     ) { | ||||
|         super() | ||||
|         options = options ?? {} | ||||
|         this._background = options?.background ?? new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto) | ||||
|         this._location = options?.location ?? new UIEventSource<Loc>(undefined) | ||||
|         this._id = "minimap" + Minimap._nextId; | ||||
|         this._allowMoving = options.allowMoving ?? true; | ||||
|         Minimap._nextId++ | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected InnerConstructElement(): HTMLElement { | ||||
|         const div = document.createElement("div") | ||||
|         div.id = this._id; | ||||
|         div.style.height = "100%" | ||||
|         div.style.width = "100%" | ||||
|         div.style.minWidth = "40px" | ||||
|         div.style.minHeight = "40px" | ||||
|         const wrapper = document.createElement("div") | ||||
|         wrapper.appendChild(div) | ||||
|         const self = this; | ||||
|         // @ts-ignore
 | ||||
|         const resizeObserver = new ResizeObserver(_ => { | ||||
|             console.log("Change in size detected!") | ||||
|             self.InitMap(); | ||||
|             self.leafletMap?.data?.invalidateSize() | ||||
|         }); | ||||
| 
 | ||||
|         resizeObserver.observe(div); | ||||
|         return wrapper; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private InitMap() { | ||||
|         if (this._constructedHtmlElement === undefined) { | ||||
|             // This element isn't initialized yet
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (document.getElementById(this._id) === null) { | ||||
|             // not yet attached, we probably got some other event
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (this._isInited) { | ||||
|             return; | ||||
|         } | ||||
|         this._isInited = true; | ||||
|         const location = this._location; | ||||
| 
 | ||||
|         let currentLayer = this._background.data.layer() | ||||
|         const map = L.map(this._id, { | ||||
|             center: [location.data?.lat ?? 0, location.data?.lon ?? 0], | ||||
|             zoom: location.data?.zoom ?? 2, | ||||
|             layers: [currentLayer], | ||||
|             zoomControl: false, | ||||
|             attributionControl: false, | ||||
|             dragging: this._allowMoving, | ||||
|             scrollWheelZoom: this._allowMoving, | ||||
|             doubleClickZoom: this._allowMoving, | ||||
|             keyboard: this._allowMoving, | ||||
|             touchZoom: this._allowMoving | ||||
|         }); | ||||
| 
 | ||||
|         map.setMaxBounds( | ||||
|             [[-100, -200], [100, 200]] | ||||
|         ); | ||||
| 
 | ||||
|         this._background.addCallbackAndRun(layer => { | ||||
|             const newLayer = layer.layer() | ||||
|             if (currentLayer !== undefined) { | ||||
|                 map.removeLayer(currentLayer); | ||||
|             } | ||||
|             currentLayer = newLayer; | ||||
|             map.addLayer(newLayer); | ||||
|         }) | ||||
| 
 | ||||
| 
 | ||||
|         let isRecursing = false; | ||||
|         map.on("moveend", function () { | ||||
|             if (isRecursing) { | ||||
|                 return | ||||
|             } | ||||
|             if (map.getZoom() === location.data.zoom && | ||||
|                 map.getCenter().lat === location.data.lat && | ||||
|                 map.getCenter().lng === location.data.lon) { | ||||
|                 return; | ||||
|             } | ||||
|             console.trace(map.getZoom(), map.getCenter(), location.data) | ||||
| 
 | ||||
|             location.data.zoom = map.getZoom(); | ||||
|             location.data.lat = map.getCenter().lat; | ||||
|             location.data.lon = map.getCenter().lng; | ||||
|             isRecursing = true; | ||||
|             location.ping(); | ||||
|             isRecursing = false; // This is ugly, I know
 | ||||
|         }) | ||||
| 
 | ||||
| 
 | ||||
|         location.addCallback(loc => { | ||||
|             const mapLoc = map.getCenter() | ||||
|             const dlat = Math.abs(loc.lat - mapLoc[0]) | ||||
|             const dlon = Math.abs(loc.lon - mapLoc[1]) | ||||
| 
 | ||||
|             if (dlat < 0.000001 && dlon < 0.000001 && map.getZoom() === loc.zoom) { | ||||
|                 return; | ||||
|             } | ||||
|             map.setView([loc.lat, loc.lon], loc.zoom) | ||||
|         }) | ||||
| 
 | ||||
|         location.map(loc => loc.zoom) | ||||
|             .addCallback(zoom => { | ||||
|                 if (Math.abs(map.getZoom() - zoom) > 0.1) { | ||||
|                     map.setZoom(zoom, {}); | ||||
|                 } | ||||
|             }) | ||||
| 
 | ||||
| 
 | ||||
|         this.leafletMap.setData(map) | ||||
|     } | ||||
| } | ||||
|  | @ -14,10 +14,14 @@ export class Basemap { | |||
|                 currentLayer: UIEventSource<BaseLayer>, | ||||
|                 lastClickLocation?: UIEventSource<{ lat: number, lon: number }>, | ||||
|                 extraAttribution?: BaseUIElement) { | ||||
| 
 | ||||
|         console.log("Currentlayer is" ,currentLayer, currentLayer.data, currentLayer.data?.id) | ||||
|         let previousLayer = currentLayer.data.layer(); | ||||
| 
 | ||||
|         this.map = L.map(leafletElementId, { | ||||
|             center: [location.data.lat ?? 0, location.data.lon ?? 0], | ||||
|             zoom: location.data.zoom ?? 2, | ||||
|             layers: [currentLayer.data.layer], | ||||
|             layers: [previousLayer], | ||||
|             zoomControl: false, | ||||
|             attributionControl: extraAttribution !== undefined | ||||
|         }); | ||||
|  | @ -42,16 +46,16 @@ export class Basemap { | |||
|         extraAttribution.AttachTo('leaflet-attribution') | ||||
|         const self = this; | ||||
| 
 | ||||
|         let previousLayer = currentLayer.data; | ||||
|         currentLayer.addCallbackAndRun(layer => { | ||||
|             if (layer === previousLayer) { | ||||
|             const newLayer = layer.layer() | ||||
|             if (newLayer === previousLayer) { | ||||
|                 return; | ||||
|             } | ||||
|             if (previousLayer !== undefined) { | ||||
|                 self.map.removeLayer(previousLayer.layer); | ||||
|                 self.map.removeLayer(previousLayer); | ||||
|             } | ||||
|             previousLayer = layer; | ||||
|             self.map.addLayer(layer.layer); | ||||
|             previousLayer = newLayer; | ||||
|             self.map.addLayer(newLayer); | ||||
|         }) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,21 +2,30 @@ import {InputElement} from "./InputElement"; | |||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import Svg from "../../Svg"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Selects a direction in degrees | ||||
|  */ | ||||
| export default class DirectionInput extends InputElement<string> { | ||||
|     public static constructMinimap: ((any) => BaseUIElement); | ||||
|     private readonly _location: UIEventSource<Loc>; | ||||
| 
 | ||||
|     public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     private readonly value: UIEventSource<string>; | ||||
|     private background; | ||||
| 
 | ||||
|     constructor(value?: UIEventSource<string>) { | ||||
|     constructor(mapBackground: UIEventSource<any>,  | ||||
|                 location: UIEventSource<Loc>, | ||||
|                 value?: UIEventSource<string>) { | ||||
|         super(); | ||||
|         this._location = location; | ||||
|         this.value = value ?? new UIEventSource<string>(undefined); | ||||
| 
 | ||||
|         this.background = mapBackground; | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<string> { | ||||
|  | @ -30,16 +39,23 @@ export default class DirectionInput extends InputElement<string> { | |||
| 
 | ||||
|     protected InnerConstructElement(): HTMLElement { | ||||
| 
 | ||||
|         let map: BaseUIElement = new FixedUiElement("") | ||||
|         if (!Utils.runningFromConsole) { | ||||
|             map = DirectionInput.constructMinimap({ | ||||
|                 background: this.background, | ||||
|                 allowMoving: false, | ||||
|                 location: this._location | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         const element = new Combine([ | ||||
|             new FixedUiElement("").SetClass("w-full h-full absolute top-0 left-O rounded-full"), | ||||
|             Svg.direction_svg().SetStyle( | ||||
|              Svg.direction_stroke_svg().SetStyle( | ||||
|                 `position: absolute;top: 0;left: 0;width: 100%;height: 100%;transform:rotate(${this.value.data ?? 0}deg);`) | ||||
|                 .SetClass("direction-svg"), | ||||
|             Svg.compass_svg().SetStyle( | ||||
|                 "position: absolute;top: 0;left: 0;width: 100%;height: 100%;") | ||||
|                 .SetClass("direction-svg relative") | ||||
|                  .SetStyle("z-index: 1000"), | ||||
|             map.SetClass("w-full h-full absolute top-0 left-O rounded-full overflow-hidden"), | ||||
|         ]) | ||||
|             .SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") | ||||
|             .SetStyle("position:relative;display:block;width: min(100%, 25em); height: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em") | ||||
|             .ConstructElement() | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| import {DropDown} from "./DropDown"; | ||||
| import * as EmailValidator from "email-validator"; | ||||
| import {parsePhoneNumberFromString} from "libphonenumber-js"; | ||||
| import InputElementMap from "./InputElementMap"; | ||||
| import {InputElement} from "./InputElement"; | ||||
| import {TextField} from "./TextField"; | ||||
| import {UIElement} from "../UIElement"; | ||||
|  | @ -12,6 +11,7 @@ import OpeningHoursInput from "../OpeningHours/OpeningHoursInput"; | |||
| import DirectionInput from "./DirectionInput"; | ||||
| import ColorPicker from "./ColorPicker"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| 
 | ||||
| interface TextFieldDef { | ||||
|     name: string, | ||||
|  | @ -19,7 +19,8 @@ interface TextFieldDef { | |||
|     isValid: ((s: string, country?: () => string) => boolean), | ||||
|     reformat?: ((s: string, country?: () => string) => string), | ||||
|     inputHelper?: (value: UIEventSource<string>, options?: { | ||||
|         location: [number, number] | ||||
|         location: [number, number], | ||||
|         mapBackgroundLayer?: UIEventSource<any> | ||||
|     }) => InputElement<string>, | ||||
| 
 | ||||
|     inputmode?: string | ||||
|  | @ -118,8 +119,12 @@ export default class ValidatedTextField { | |||
|                 str = "" + str; | ||||
|                 return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360 | ||||
|             }, str => str, | ||||
|             (value) => { | ||||
|                 return new DirectionInput(value); | ||||
|             (value, options) => { | ||||
|                 return new DirectionInput(options.mapBackgroundLayer , new UIEventSource<Loc>({ | ||||
|                     lat: options.location[0], | ||||
|                     lon: options.location[1], | ||||
|                     zoom: 19 | ||||
|                 }),value); | ||||
|             }, | ||||
|             "numeric" | ||||
|         ), | ||||
|  | @ -235,7 +240,8 @@ export default class ValidatedTextField { | |||
|         textAreaRows?: number, | ||||
|         isValid?: ((s: string, country: () => string) => boolean), | ||||
|         country?: () => string, | ||||
|         location?: [number /*lat*/, number /*lon*/] | ||||
|         location?: [number /*lat*/, number /*lon*/], | ||||
|         mapBackgroundLayer?: UIEventSource<any> | ||||
|     }): InputElement<string> { | ||||
|         options = options ?? {}; | ||||
|         options.placeholder = options.placeholder ?? type; | ||||
|  | @ -269,87 +275,12 @@ export default class ValidatedTextField { | |||
| 
 | ||||
|         if (tp.inputHelper) { | ||||
|             input = new CombinedInputElement(input, tp.inputHelper(input.GetValue(), { | ||||
|                 location: options.location | ||||
|                 location: options.location, | ||||
|                 mapBackgroundLayer: options.mapBackgroundLayer | ||||
|             })); | ||||
|         } | ||||
|         return input; | ||||
|     } | ||||
| 
 | ||||
|     public static NumberInput(type: string = "int", extraValidation: (number: Number) => boolean = undefined): InputElement<number> { | ||||
|         const isValid = ValidatedTextField.AllTypes[type].isValid; | ||||
|         extraValidation = extraValidation ?? (() => true) | ||||
| 
 | ||||
|         const fromString = str => { | ||||
|             if (!isValid(str)) { | ||||
|                 return undefined; | ||||
|             } | ||||
|             const n = Number(str); | ||||
|             if (!extraValidation(n)) { | ||||
|                 return undefined; | ||||
|             } | ||||
|             return n; | ||||
|         }; | ||||
|         const toString = num => { | ||||
|             if (num === undefined) { | ||||
|                 return undefined; | ||||
|             } | ||||
|             return "" + num; | ||||
|         }; | ||||
|         const textField = ValidatedTextField.InputForType(type); | ||||
|         return new InputElementMap(textField, (n0, n1) => n0 === n1, fromString, toString) | ||||
|     } | ||||
| 
 | ||||
|     public static KeyInput(allowEmpty: boolean = false): InputElement<string> { | ||||
| 
 | ||||
|         function fromString(str) { | ||||
|             if (str?.match(/^[a-zA-Z][a-zA-Z0-9:_-]*$/)) { | ||||
|                 return str; | ||||
|             } | ||||
|             if (str === "" && allowEmpty) { | ||||
|                 return ""; | ||||
|             } | ||||
| 
 | ||||
|             return undefined | ||||
|         } | ||||
| 
 | ||||
|         const toString = str => str | ||||
| 
 | ||||
|         function isSame(str0, str1) { | ||||
|             return str0 === str1; | ||||
|         } | ||||
| 
 | ||||
|         const textfield = new TextField({ | ||||
|             placeholder: "key", | ||||
|             isValid: str => fromString(str) !== undefined, | ||||
|             value: new UIEventSource<string>("") | ||||
|         }); | ||||
| 
 | ||||
|         return new InputElementMap(textfield, isSame, fromString, toString); | ||||
|     } | ||||
| 
 | ||||
|     static Mapped<T>(fromString: (str) => T, toString: (T) => string, options?: { | ||||
|         placeholder?: string | UIElement, | ||||
|         type?: string, | ||||
|         value?: UIEventSource<string>, | ||||
|         startValidated?: boolean, | ||||
|         textArea?: boolean, | ||||
|         textAreaRows?: number, | ||||
|         isValid?: ((string: string) => boolean), | ||||
|         country?: () => string | ||||
|     }): InputElement<T> { | ||||
|         let textField: InputElement<string>; | ||||
|         if (options?.type) { | ||||
|             textField = ValidatedTextField.InputForType(options.type, options); | ||||
|         } else { | ||||
|             textField = new TextField(options); | ||||
|         } | ||||
|         return new InputElementMap( | ||||
|             textField, (a, b) => a === b, | ||||
|             fromString, toString | ||||
|         ); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static HelpText(): string { | ||||
|         const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n") | ||||
|         return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations | ||||
|  | @ -360,7 +291,8 @@ export default class ValidatedTextField { | |||
|                       isValid?: ((s: string, country?: () => string) => boolean), | ||||
|                       reformat?: ((s: string, country?: () => string) => string), | ||||
|                       inputHelper?: (value: UIEventSource<string>, options?: { | ||||
|                           location: [number, number] | ||||
|                           location: [number, number], | ||||
|                           mapBackgroundLayer: UIEventSource<any> | ||||
|                       }) => InputElement<string>, | ||||
|                       inputmode?: string): TextFieldDef { | ||||
| 
 | ||||
|  |  | |||
|  | @ -326,7 +326,8 @@ export default class TagRenderingQuestion extends UIElement { | |||
|         const textField = ValidatedTextField.InputForType(this._configuration.freeform.type, { | ||||
|             isValid: (str) => (str.length <= 255), | ||||
|             country: () => this._tags.data._country, | ||||
|             location: [this._tags.data._lat, this._tags.data._lon] | ||||
|             location: [this._tags.data._lat, this._tags.data._lon], | ||||
|             mapBackgroundLayer: State.state.backgroundLayer | ||||
|         }); | ||||
| 
 | ||||
|         textField.GetValue().setData(this._tags.data[this._configuration.freeform.key]); | ||||
|  |  | |||
|  | @ -15,13 +15,15 @@ export default class ShowDataLayer { | |||
|     private _layerDict; | ||||
|     private readonly _leafletMap: UIEventSource<L.Map>; | ||||
|     private _cleanCount = 0; | ||||
|     private readonly _enablePopups: boolean; | ||||
| 
 | ||||
|     constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>, | ||||
|                 leafletMap: UIEventSource<L.Map>, | ||||
|                 layoutToUse: UIEventSource<LayoutConfig>) { | ||||
|                 layoutToUse: UIEventSource<LayoutConfig>, | ||||
|                 enablePopups= true) { | ||||
|         this._leafletMap = leafletMap; | ||||
|         this._enablePopups = enablePopups; | ||||
|         const self = this; | ||||
|         const mp = leafletMap.data; | ||||
|         self._layerDict = {}; | ||||
| 
 | ||||
|         layoutToUse.addCallbackAndRun(layoutToUse => { | ||||
|  | @ -39,7 +41,9 @@ export default class ShowDataLayer { | |||
|             if (features.data === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|             if (leafletMap.data === undefined) { | ||||
|             const mp = leafletMap.data; | ||||
| 
 | ||||
|             if(mp === undefined){ | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|  | @ -119,6 +123,11 @@ export default class ShowDataLayer { | |||
|             // No popup action defined -> Don't do anything
 | ||||
|             return; | ||||
|         } | ||||
|         if(!this._enablePopups){ | ||||
|             // Probably a map in the popup - no popups needed!
 | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         const popup = L.popup({ | ||||
|             autoPan: true, | ||||
|             closeOnEscapeKey: true, | ||||
|  | @ -171,15 +180,15 @@ export default class ShowDataLayer { | |||
|     } | ||||
| 
 | ||||
|     private CreateGeojsonLayer(): L.Layer { | ||||
|         const self = this; | ||||
|         const data = { | ||||
|             type: "FeatureCollection", | ||||
|             features: [] | ||||
|         } | ||||
|         // @ts-ignore
 | ||||
|         return L.geoJSON(data, { | ||||
|             style: feature => self.createStyleFor(feature), | ||||
|             pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng), | ||||
|             const self = this; | ||||
|             const data = { | ||||
|                 type: "FeatureCollection", | ||||
|                 features: [] | ||||
|             } | ||||
|             // @ts-ignore
 | ||||
|             return L.geoJSON(data, { | ||||
|                 style: feature => self.createStyleFor(feature), | ||||
|                 pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng), | ||||
|             onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer) | ||||
|         }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,9 @@ import LayerConfig from "../Customizations/JSON/LayerConfig"; | |||
| import Title from "./Base/Title"; | ||||
| import Table from "./Base/Table"; | ||||
| import Histogram from "./BigComponents/Histogram"; | ||||
| import Loc from "../Models/Loc"; | ||||
| import ShowDataLayer from "./ShowDataLayer"; | ||||
| import Minimap from "./Base/Minimap"; | ||||
| 
 | ||||
| export default class SpecialVisualizations { | ||||
| 
 | ||||
|  | @ -32,7 +35,6 @@ export default class SpecialVisualizations { | |||
|         example?: string, | ||||
|         args: { name: string, defaultValue?: string, doc: string }[] | ||||
|     }[] = | ||||
| 
 | ||||
|         [{ | ||||
|             funcName: "all_tags", | ||||
|             docs: "Prints all key-value pairs of the object - used for debugging", | ||||
|  | @ -85,7 +87,57 @@ export default class SpecialVisualizations { | |||
|                     return new ImageUploadFlow(tags, args[0]) | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "minimap", | ||||
|                 docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div", | ||||
|                 args: [ | ||||
|                     { | ||||
|                         doc: "The zoomlevel: the higher, the more zoomed in with 1 being the entire world and 19 being really close", | ||||
|                         name: "zoomlevel", | ||||
|                         defaultValue: "18" | ||||
|                     } | ||||
|                 ], | ||||
|                 example: "`{minimap()}`", | ||||
|                 constr: (state, tagSource, args) => { | ||||
|                     const properties = tagSource.data; | ||||
|                     const feature = state.allElements.ContainingFeatures.get(properties.id) | ||||
| 
 | ||||
|                     let zoom = 18 | ||||
|                     if(args[0] ){ | ||||
|                         const parsed = Number(args[0]) | ||||
|                         if(!isNaN(parsed) && parsed > 0 && parsed < 25){ | ||||
|                             zoom = parsed; | ||||
|                         } | ||||
|                     } | ||||
|                     const minimap = new Minimap( | ||||
|                         { | ||||
|                             background: state.backgroundLayer, | ||||
|                             location: new UIEventSource<Loc>({ | ||||
|                                 lat: Number(properties._lat), | ||||
|                                 lon: Number(properties._lon), | ||||
|                                 zoom: zoom | ||||
|                             }), | ||||
|                             allowMoving: false | ||||
|                         } | ||||
|                     ) | ||||
| 
 | ||||
|                     new ShowDataLayer( | ||||
|                         new UIEventSource<{ feature: any; freshness: Date }[]>([ | ||||
|                             { | ||||
|                                 freshness: new Date(), | ||||
|                                 feature: feature | ||||
|                             } | ||||
|                         ]), | ||||
|                         minimap.leafletMap, | ||||
|                         State.state.layoutToUse, | ||||
|                         false | ||||
|                     ) | ||||
| 
 | ||||
|                     minimap.SetStyle("overflow: hidden; pointer-events: none;") | ||||
|                     return minimap; | ||||
| 
 | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "reviews", | ||||
|                 docs: "Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten", | ||||
|  |  | |||
|  | @ -17,10 +17,10 @@ export class SubstitutedTranslation extends VariableUiElement { | |||
|         super( | ||||
|             tagsSource.map(tags => { | ||||
|                 const txt = Utils.SubstituteKeys(translation.txt, tags) | ||||
|                if (txt === undefined) { | ||||
|                 if (txt === undefined) { | ||||
|                     return undefined | ||||
|                 } | ||||
|                return new Combine(SubstitutedTranslation.EvaluateSpecialComponents(txt, tagsSource)) | ||||
|                 return new Combine(SubstitutedTranslation.EvaluateSpecialComponents(txt, tagsSource)) | ||||
|             }, [Locale.language]) | ||||
|         ) | ||||
| 
 | ||||
|  | @ -34,13 +34,14 @@ export class SubstitutedTranslation extends VariableUiElement { | |||
|         for (const knownSpecial of SpecialVisualizations.specialVisualizations) { | ||||
| 
 | ||||
|             // Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
 | ||||
|             const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)}(.*)`); | ||||
|             const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`); | ||||
|             if (matched != null) { | ||||
| 
 | ||||
|                 // We found a special component that should be brought to live
 | ||||
|                 const partBefore = SubstitutedTranslation.EvaluateSpecialComponents(matched[1], tags); | ||||
|                 const argument = matched[2].trim(); | ||||
|                 const partAfter = SubstitutedTranslation.EvaluateSpecialComponents(matched[3], tags); | ||||
|                 const style = matched[3] ?? "" | ||||
|                 const partAfter = SubstitutedTranslation.EvaluateSpecialComponents(matched[4], tags); | ||||
|                 try { | ||||
|                     const args = knownSpecial.args.map(arg => arg.defaultValue ?? ""); | ||||
|                     if (argument.length > 0) { | ||||
|  | @ -56,9 +57,10 @@ export class SubstitutedTranslation extends VariableUiElement { | |||
| 
 | ||||
| 
 | ||||
|                     let element: BaseUIElement = new FixedUiElement(`Constructing ${knownSpecial}(${args.join(", ")})`) | ||||
|                     try{ | ||||
|                       element =  knownSpecial.constr(State.state, tags, args); | ||||
|                     }catch(e){ | ||||
|                     try { | ||||
|                         element = knownSpecial.constr(State.state, tags, args); | ||||
|                         element.SetStyle(style) | ||||
|                     } catch (e) { | ||||
|                         console.error("SPECIALRENDERING FAILED for", tags.data.id, e) | ||||
|                         element = new FixedUiElement(`Could not generate special rendering for ${knownSpecial}(${args.join(", ")}) ${e}`).SetClass("alert") | ||||
|                     } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
|     "render": "{reviews()}" | ||||
|   }, | ||||
|   "minimap": { | ||||
|     "render": "<div class='w-full h-6 rounded-full'>{minimap(19, {id})}</div>" | ||||
|     "render": "{minimap(19, id): width:100%; height:6rem; border-radius:999rem; overflow: hidden; pointer-events: none;}" | ||||
|   }, | ||||
|   "phone": { | ||||
|     "question": { | ||||
|  |  | |||
							
								
								
									
										6
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								index.ts
									
										
									
									
									
								
							|  | @ -14,10 +14,12 @@ import Translations from "./UI/i18n/Translations"; | |||
| import CountryCoder from "latlon2country" | ||||
| 
 | ||||
| import SimpleMetaTagger from "./Logic/SimpleMetaTagger"; | ||||
| import Minimap from "./UI/Base/Minimap"; | ||||
| import DirectionInput from "./UI/Input/DirectionInput"; | ||||
| 
 | ||||
| // Workaround for a stupid crash: inject the function
 | ||||
| // Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts
 | ||||
| SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); | ||||
| 
 | ||||
| DirectionInput.constructMinimap = options =>  new Minimap(options) | ||||
| 
 | ||||
| let defaultLayout = "" | ||||
| // --------------------- Special actions based on the parameters -----------------
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| <head> | ||||
|     <title>Small tests</title> | ||||
|     <link href="index.css" rel="stylesheet"/> | ||||
|     <link rel="stylesheet" href="./vendor/leaflet.css"/> | ||||
|     <link href="css/tabbedComponent.css" rel="stylesheet"/> | ||||
|     <link href="css/openinghourstable.css" rel="stylesheet"/> | ||||
|     <link href="css/tagrendering.css" rel="stylesheet"/> | ||||
|  |  | |||
							
								
								
									
										97
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										97
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -10,11 +10,15 @@ import {FixedUiElement} from "./UI/Base/FixedUiElement"; | |||
| import Img from "./UI/Base/Img"; | ||||
| import {AttributedImage} from "./UI/Image/AttributedImage"; | ||||
| import {Imgur} from "./Logic/ImageProviders/Imgur"; | ||||
| import ReviewForm from "./UI/Reviews/ReviewForm"; | ||||
| import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||
| import Minimap from "./UI/Base/Minimap"; | ||||
| import Loc from "./Models/Loc"; | ||||
| import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; | ||||
| import ShowDataLayer from "./UI/ShowDataLayer"; | ||||
| import LayoutConfig from "./Customizations/JSON/LayoutConfig"; | ||||
| import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; | ||||
| 
 | ||||
| 
 | ||||
| function TestSlideshow(){ | ||||
| function TestSlideshow() { | ||||
|     const elems = new UIEventSource([ | ||||
|         new FixedUiElement("A"), | ||||
|         new FixedUiElement("qmsldkfjqmlsdkjfmqlskdjfmqlksdf").SetClass("text-xl"), | ||||
|  | @ -25,17 +29,17 @@ function TestSlideshow(){ | |||
|     new SlideShow(elems).AttachTo("maindiv") | ||||
| } | ||||
| 
 | ||||
| function TestTagRendering(){ | ||||
| function TestTagRendering() { | ||||
|     State.state = new State(undefined) | ||||
|     const tagsSource = new UIEventSource({ | ||||
|         id:"node/1" | ||||
|         id: "node/1" | ||||
|     }) | ||||
|     new TagRenderingQuestion( | ||||
|         tagsSource, | ||||
|         new TagRenderingConfig({ | ||||
|             multiAnswer: false, | ||||
|             freeform: { | ||||
|                 key:"valve" | ||||
|                 key: "valve" | ||||
|             }, | ||||
|             question: "What valves are supported?", | ||||
|             render: "This pump supports {valve}", | ||||
|  | @ -45,8 +49,8 @@ function TestTagRendering(){ | |||
|                     then: "This pump supports dunlop" | ||||
|                 }, | ||||
|                 { | ||||
|                     if:"valve=shrader", | ||||
|                     then:"shrader is supported", | ||||
|                     if: "valve=shrader", | ||||
|                     then: "shrader is supported", | ||||
|                 } | ||||
|             ], | ||||
| 
 | ||||
|  | @ -55,7 +59,7 @@ function TestTagRendering(){ | |||
|     new VariableUiElement(tagsSource.map(tags => tags["valves"])).SetClass("alert").AttachTo("extradiv") | ||||
| } | ||||
| 
 | ||||
| function TestAllInputMethods(){ | ||||
| function TestAllInputMethods() { | ||||
| 
 | ||||
|     new Combine(ValidatedTextField.tpList.map(tp => { | ||||
|         const tf = ValidatedTextField.InputForType(tp.name); | ||||
|  | @ -64,6 +68,75 @@ function TestAllInputMethods(){ | |||
|     })).AttachTo("maindiv") | ||||
| } | ||||
| 
 | ||||
| new ReviewForm(() => { | ||||
|     return undefined; | ||||
| }, new OsmConnection(true, new UIEventSource<string>(undefined), "test")).AttachTo("maindiv"); | ||||
| 
 | ||||
| const location = new UIEventSource<Loc>({ | ||||
|     lon: 4.84771728515625, | ||||
|     lat: 51.17920846421931, | ||||
|     zoom: 14 | ||||
| }) | ||||
| const map0 = new Minimap({ | ||||
|     location: location, | ||||
|     allowMoving: true, | ||||
|     background: new AvailableBaseLayers(location).availableEditorLayers.map(layers => layers[2]) | ||||
| }) | ||||
| map0.SetStyle("width: 500px; height: 250px; overflow: hidden; border: 2px solid red") | ||||
|     .AttachTo("maindiv") | ||||
| 
 | ||||
| const layout = AllKnownLayouts.layoutsList[1] | ||||
| State.state = new State(layout) | ||||
| console.log("LAYOUT is", layout.id) | ||||
| 
 | ||||
| const feature = { | ||||
|         "type": "Feature", | ||||
|         _matching_layer_id: "bike_repair_station", | ||||
|         "properties": { | ||||
|             id: "node/-1", | ||||
|             "amenity": "bicycle_repair_station" | ||||
|         }, | ||||
|         "geometry": { | ||||
|             "type": "Point", | ||||
|             "coordinates": [ | ||||
|                 4.84771728515625, | ||||
|                 51.17920846421931 | ||||
|             ] | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| ; | ||||
| 
 | ||||
| State.state.allElements.addOrGetElement(feature) | ||||
| 
 | ||||
| const featureSource = new UIEventSource([{ | ||||
|     freshness: new Date(), | ||||
|     feature: feature | ||||
| }]) | ||||
| 
 | ||||
| new ShowDataLayer( | ||||
|     featureSource, | ||||
|     map0.leafletMap, | ||||
|     new UIEventSource<LayoutConfig>(layout) | ||||
| ) | ||||
| 
 | ||||
| const map1 = new Minimap({ | ||||
|         location: location, | ||||
|         allowMoving: true, | ||||
|         background: new AvailableBaseLayers(location).availableEditorLayers.map(layers => layers[5]) | ||||
|     }, | ||||
| ) | ||||
| 
 | ||||
| map1.SetStyle("width: 500px; height: 250px; overflow: hidden; border : 2px solid black") | ||||
|     .AttachTo("extradiv") | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| new ShowDataLayer( | ||||
|     featureSource, | ||||
|     map1.leafletMap, | ||||
|     new UIEventSource<LayoutConfig>(layout) | ||||
| ) | ||||
| 
 | ||||
| featureSource.ping() | ||||
| 
 | ||||
| // */
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue