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 | ||||
|      */ | ||||
|     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 | ||||
|      */ | ||||
|  |  | |||
|  | @ -4,10 +4,15 @@ import MapControlButton from "../MapControlButton"; | |||
| import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"; | ||||
| import Svg from "../../Svg"; | ||||
| 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 { | ||||
| 
 | ||||
|     constructor(state: MapState) { | ||||
|     constructor(state: MapState & { featurePipeline: FeaturePipeline }) { | ||||
| 
 | ||||
|         const geolocatioHandler = new GeoLocationHandler( | ||||
|             state | ||||
|  | @ -38,7 +43,26 @@ export default class RightControls extends Combine { | |||
|             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") | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										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 Slider from "./UI/Input/Slider"; | ||||
| 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