From 13e949a1cd17278c363722700cf1f500483b1af9 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 21 Jul 2022 15:54:24 +0200 Subject: [PATCH] Wire in level selector --- Logic/State/MapState.ts | 6 ++ UI/BigComponents/RightControls.ts | 28 ++++++++- UI/Input/LevelSelector.ts | 94 +++++++++++++++++++++++++++++++ test.ts | 24 -------- 4 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 UI/Input/LevelSelector.ts diff --git a/Logic/State/MapState.ts b/Logic/State/MapState.ts index 5a2564c06..d5e5a1fa6 100644 --- a/Logic/State/MapState.ts +++ b/Logic/State/MapState.ts @@ -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 = new UIEventSource([], "filteredLayers"); + + /** + * Filters which apply onto all layers + */ + public globalFilters: UIEventSource<{ filter: FilterState, id: string }[]> = new UIEventSource([], "globalFilters") + /** * Which overlays are shown */ diff --git a/UI/BigComponents/RightControls.ts b/UI/BigComponents/RightControls.ts index 6e3e333fa..ec2723210 100644 --- a/UI/BigComponents/RightControls.ts +++ b/UI/BigComponents/RightControls.ts @@ -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 => 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") } diff --git a/UI/Input/LevelSelector.ts b/UI/Input/LevelSelector.ts new file mode 100644 index 000000000..82e945809 --- /dev/null +++ b/UI/Input/LevelSelector.ts @@ -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{ + + private readonly _value : UIEventSource; + + constructor(currentLevels: Store, options?:{ + value?: UIEventSource + }) { + + 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(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 { + 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); + } + + +} \ No newline at end of file diff --git a/test.ts b/test.ts index d8eb427be..287016254 100644 --- a/test.ts +++ b/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")