forked from MapComplete/MapComplete
		
	Wire in level selector
This commit is contained in:
		
							parent
							
								
									0d3e7f8168
								
							
						
					
					
						commit
						13e949a1cd
					
				
					 4 changed files with 126 additions and 26 deletions
				
			
		|  | @ -78,6 +78,12 @@ export default class MapState extends UserRelatedState { | ||||||
|      * Which layers are enabled in the current theme and what filters are applied onto them |      * Which layers are enabled in the current theme and what filters are applied onto them | ||||||
|      */ |      */ | ||||||
|     public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([], "filteredLayers"); |     public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([], "filteredLayers"); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Filters which apply onto all layers | ||||||
|  |      */ | ||||||
|  |     public globalFilters: UIEventSource<{ filter: FilterState, id: string }[]> = new UIEventSource([], "globalFilters") | ||||||
|  |      | ||||||
|     /** |     /** | ||||||
|      * Which overlays are shown |      * Which overlays are shown | ||||||
|      */ |      */ | ||||||
|  |  | ||||||
|  | @ -4,10 +4,15 @@ import MapControlButton from "../MapControlButton"; | ||||||
| import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"; | import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"; | ||||||
| import Svg from "../../Svg"; | import Svg from "../../Svg"; | ||||||
| import MapState from "../../Logic/State/MapState"; | import MapState from "../../Logic/State/MapState"; | ||||||
|  | import {VariableUiElement} from "../Base/VariableUIElement"; | ||||||
|  | import LevelSelector from "../Input/LevelSelector"; | ||||||
|  | import {UIEventSource} from "../../Logic/UIEventSource"; | ||||||
|  | import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; | ||||||
|  | import {Utils} from "../../Utils"; | ||||||
| 
 | 
 | ||||||
| export default class RightControls extends Combine { | export default class RightControls extends Combine { | ||||||
| 
 | 
 | ||||||
|     constructor(state: MapState) { |     constructor(state: MapState & { featurePipeline: FeaturePipeline }) { | ||||||
| 
 | 
 | ||||||
|         const geolocatioHandler = new GeoLocationHandler( |         const geolocatioHandler = new GeoLocationHandler( | ||||||
|             state |             state | ||||||
|  | @ -38,7 +43,26 @@ export default class RightControls extends Combine { | ||||||
|             state.locationControl.ping(); |             state.locationControl.ping(); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         super([plus, min, geolocationButton].map(el => el.SetClass("m-0.5 md:m-1"))) |         const levelsInView = state.currentBounds.map(bbox => { | ||||||
|  |             if(bbox === undefined){ | ||||||
|  |                 return [] | ||||||
|  |             } | ||||||
|  |             const allElements = state.featurePipeline.GetAllFeaturesAndMetaWithin(bbox); | ||||||
|  |             const allLevelsRaw: string[] = [].concat(...allElements.map(allElements => allElements.features.map(f => <string>f.properties["level"]))) | ||||||
|  |             const allLevels = [].concat(...allLevelsRaw.map(l => LevelSelector.LevelsParser(l))) | ||||||
|  |             return Utils.Dedup(allLevels) | ||||||
|  |         }) | ||||||
|  |         const levelSelect = new LevelSelector(levelsInView) | ||||||
|  |          | ||||||
|  |         levelsInView.addCallbackAndRun(levelsInView => { | ||||||
|  |             if(levelsInView.length <= 1){ | ||||||
|  |                 levelSelect.SetClass("invisible") | ||||||
|  |             }else{ | ||||||
|  |                 levelSelect.RemoveClass("invisible") | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         super([levelSelect, plus, min, geolocationButton].map(el => el.SetClass("m-0.5 md:m-1"))) | ||||||
|         this.SetClass("flex flex-col items-center") |         this.SetClass("flex flex-col items-center") | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										94
									
								
								UI/Input/LevelSelector.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								UI/Input/LevelSelector.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | ||||||
|  | import {InputElement} from "./InputElement"; | ||||||
|  | import {Store, UIEventSource} from "../../Logic/UIEventSource"; | ||||||
|  | import Combine from "../Base/Combine"; | ||||||
|  | import Slider from "./Slider"; | ||||||
|  | import {ClickableToggle} from "./Toggle"; | ||||||
|  | import {FixedUiElement} from "../Base/FixedUiElement"; | ||||||
|  | import {Utils} from "../../Utils"; | ||||||
|  | 
 | ||||||
|  | export default class LevelSelector extends Combine implements InputElement<string>{ | ||||||
|  |      | ||||||
|  |     private readonly _value : UIEventSource<string>; | ||||||
|  |      | ||||||
|  |     constructor(currentLevels: Store<string[]>, options?:{ | ||||||
|  |         value?: UIEventSource<string> | ||||||
|  |     }) { | ||||||
|  | 
 | ||||||
|  |         const testData = ["-1", "0", "0.5", "1", "1.5", "2"] | ||||||
|  |         let slider = new Slider(0, testData.length - 1, {vertical: true}); | ||||||
|  |         slider.SetClass("flex m-1 elevatorslider mb-0 mt-8").SetStyle("height: "+2.5*testData.length+"rem ") | ||||||
|  |         const toggleClass = "flex border-2 border-blue-500 w-10 h-10 place-content-center items-center" | ||||||
|  |         const values = testData.map((data, i) => new ClickableToggle( | ||||||
|  |             new FixedUiElement(data).SetClass("active bg-subtle " + toggleClass), new FixedUiElement(data).SetClass(toggleClass), slider.GetValue().sync( | ||||||
|  |                 (sliderVal) => { | ||||||
|  |                     return sliderVal === i | ||||||
|  |                 }, | ||||||
|  |                 [], | ||||||
|  |                 (isSelected) => { | ||||||
|  |                     return isSelected ? i : slider.GetValue().data | ||||||
|  |                 } | ||||||
|  |             )) | ||||||
|  |             .ToggleOnClick() | ||||||
|  |             .SetClass("flex flex-column ml-5 bg-slate-200 w-10 h-10 valuesContainer")) | ||||||
|  | 
 | ||||||
|  |         super([new Combine(values.reverse()).SetClass("mt-8"), slider]) | ||||||
|  |         this.SetClass("flex flex-row h-14"); | ||||||
|  |          | ||||||
|  |         const value = this._value = options?.value ?? new UIEventSource<string>(undefined) | ||||||
|  |         slider.GetValue().addCallbackAndRun(i => { | ||||||
|  |             if(currentLevels?.data === undefined){ | ||||||
|  |                 return | ||||||
|  |             } | ||||||
|  |             value.setData(currentLevels?.data[i]); | ||||||
|  |         }) | ||||||
|  |         value.addCallback(level => { | ||||||
|  |             const i = currentLevels?.data?.findIndex(l => l === level) | ||||||
|  |             slider.GetValue().setData(i) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     GetValue(): UIEventSource<string> { | ||||||
|  |         return this._value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected InnerConstructElement(): HTMLElement { | ||||||
|  |         return undefined; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IsValid(t: string): boolean { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Parses a level specifier to the various available levels | ||||||
|  |      *  | ||||||
|  |      * LevelSelector.LevelsParser("0") // => ["0"]
 | ||||||
|  |      * LevelSelector.LevelsParser("1") // => ["1"]
 | ||||||
|  |      * LevelSelector.LevelsParser("0;2") // => ["0","2"]
 | ||||||
|  |      * LevelSelector.LevelsParser("0-5") // => ["0","1","2","3","4","5"]
 | ||||||
|  |      * LevelSelector.LevelsParser("0") // => ["0"]
 | ||||||
|  |      */ | ||||||
|  |     public static LevelsParser(level: string): string[] { | ||||||
|  |         let spec = [level] | ||||||
|  |         spec = [].concat(...spec.map(s => s.split(";"))) | ||||||
|  |         spec = [].concat(...spec.map(s => { | ||||||
|  |             s = s.trim() | ||||||
|  |             if(s.indexOf("-") < 0){ | ||||||
|  |                 return s | ||||||
|  |             } | ||||||
|  |             const [start, end] = s.split("-").map(s => Number(s.trim())) | ||||||
|  |             if(isNaN(start) || isNaN(end)){ | ||||||
|  |                 return undefined | ||||||
|  |             } | ||||||
|  |             const values = [] | ||||||
|  |             for (let i = start; i <= end; i++) { | ||||||
|  |                 values.push(i+"") | ||||||
|  |             } | ||||||
|  |             return values | ||||||
|  |         })) | ||||||
|  |         return Utils.NoNull(spec); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |      | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -6,27 +6,3 @@ import { VariableUiElement } from "./UI/Base/VariableUIElement"; | ||||||
| import { FixedInputElement } from "./UI/Input/FixedInputElement"; | import { FixedInputElement } from "./UI/Input/FixedInputElement"; | ||||||
| import Slider from "./UI/Input/Slider"; | import Slider from "./UI/Input/Slider"; | ||||||
| import Toggle, { ClickableToggle } from "./UI/Input/Toggle"; | import Toggle, { ClickableToggle } from "./UI/Input/Toggle"; | ||||||
| 
 |  | ||||||
| const testData = ["-1", "0", "0.5", "1", "1.5", "2"] |  | ||||||
| let slider = new Slider(0, testData.length - 1, {vertical: true}); |  | ||||||
| 
 |  | ||||||
| slider.SetClass("flex m-1 elevatorslider mb-0 mt-8").SetStyle("height: "+2.5*testData.length+"rem ") |  | ||||||
| 
 |  | ||||||
| const toggleClass = "flex border-2 border-blue-500 w-10 h-10 place-content-center items-center" |  | ||||||
| 
 |  | ||||||
| const values = testData.map((data, i) => new ClickableToggle( |  | ||||||
|   new FixedUiElement(data).SetClass("active bg-subtle " + toggleClass), new FixedUiElement(data).SetClass(toggleClass), slider.GetValue().sync( |  | ||||||
|     (sliderVal) => { |  | ||||||
|       return sliderVal === i |  | ||||||
|     }, |  | ||||||
|     [], |  | ||||||
|     (isSelected) => { |  | ||||||
|       return isSelected ? i : slider.GetValue().data |  | ||||||
|     } |  | ||||||
|   )) |  | ||||||
|   .ToggleOnClick() |  | ||||||
|   .SetClass("flex flex-column ml-5 bg-slate-200 w-10 h-10 valuesContainer")) |  | ||||||
| 
 |  | ||||||
| const valCombine = new Combine(values.reverse()) |  | ||||||
| 
 |  | ||||||
| new Combine([valCombine.SetClass("mt-8"), slider]).SetClass("flex flex-row h-14").AttachTo("extradiv") |  | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue