From 263cef5750891eae01a8baadc086d66d79ae9bad Mon Sep 17 00:00:00 2001 From: Ward Date: Mon, 26 Jul 2021 17:45:54 +0200 Subject: [PATCH] styling checkbox and selection --- Logic/FeatureSource/FilteringFeatureSource.ts | 1 - UI/BigComponents/FilterView.ts | 46 +-- UI/Input/Checkboxes.ts | 164 +++++----- UI/Input/RadioButton.ts | 302 ++++++++++-------- .../public_bookcase/public_bookcase.json | 28 ++ 5 files changed, 304 insertions(+), 237 deletions(-) diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts index 87c89c8a1..61e781113 100644 --- a/Logic/FeatureSource/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/FilteringFeatureSource.ts @@ -74,7 +74,6 @@ export default class FilteringFeatureSource implements FeatureSource { if (FilteringFeatureSource.showLayer(layer, location)) { const tagsFilter = layer.appliedFilters.data; - if (tagsFilter) { const properties = f.feature.properties; if (!tagsFilter.matchesProperties(properties)) { diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index 918b26f31..58dda3140 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -1,18 +1,12 @@ import { Utils } from "./../../Utils"; import { FixedInputElement } from "./../Input/FixedInputElement"; import { RadioButton } from "./../Input/RadioButton"; -import { FixedUiElement } from "./../Base/FixedUiElement"; -import { LayerConfigJson } from "./../../Customizations/JSON/LayerConfigJson"; -import { UIEventSource } from "../../Logic/UIEventSource"; import { VariableUiElement } from "../Base/VariableUIElement"; -import State from "../../State"; import Toggle from "../Input/Toggle"; import Combine from "../Base/Combine"; import Translations from "../i18n/Translations"; import LayerConfig from "../../Customizations/JSON/LayerConfig"; -import BaseUIElement from "../BaseUIElement"; import { Translation } from "../i18n/Translation"; -import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import Svg from "../../Svg"; import FilterConfig from "../../Customizations/JSON/FilterConfig"; import CheckBoxes from "../Input/Checkboxes"; @@ -20,6 +14,7 @@ import { InputElement } from "../Input/InputElement"; import { TagsFilter } from "../../Logic/Tags/TagsFilter"; import InputElementMap from "../Input/InputElementMap"; import { And } from "../../Logic/Tags/And"; +import { UIEventSource } from "../../Logic/UIEventSource"; /** * Shows the filter @@ -47,7 +42,8 @@ export default class FilterView extends VariableUiElement { return; } - const style = "display:flex;align-items:center;color:#007759"; + const style = + "display:flex;align-items:center;color:#007759;padding:0.5rem 0;"; const name: Translation = Translations.WT( filteredLayer.layerDef.name @@ -61,23 +57,24 @@ export default class FilterView extends VariableUiElement { .Clone() .SetStyle("font-size:large;padding-left:1.25rem"); - const layerChecked = new Combine([icon, styledNameChecked]).SetStyle(style); + const layerChecked = new Combine([icon, styledNameChecked]) + .SetStyle(style) + .onClick(() => filteredLayer.isDisplayed.setData(false)); - const layerNotChecked = new Combine([ - iconUnselected, - styledNameUnChecked, - ]).SetStyle(style); + const layerNotChecked = new Combine([iconUnselected, styledNameUnChecked]) + .SetStyle(style) + .onClick(() => filteredLayer.isDisplayed.setData(true)); let listFilterElements: InputElement[] = layer.filters.map( FilterView.createFilter ); - function update() { + const update = () => { let listTagsFilters = Utils.NoNull( listFilterElements.map((input) => input.GetValue().data) ); - filteredLayer.appliedTags.setData(new And(listTagsFilters)); - } + filteredLayer.appliedFilters.setData(new And(listTagsFilters)); + }; listFilterElements.forEach((inputElement) => inputElement.GetValue().addCallback((_) => update()) @@ -87,15 +84,20 @@ export default class FilterView extends VariableUiElement { new Combine([layerChecked, ...listFilterElements]), layerNotChecked, filteredLayer.isDisplayed - ) - .ToggleOnClick() - .SetStyle("margin:0.3em;"); + ).SetStyle("margin:0.3em;"); } static createFilter(filterConfig: FilterConfig): InputElement { if (filterConfig.options.length === 1) { let option = filterConfig.options[0]; - let checkboxes = new CheckBoxes([option.question.Clone()]); + let checkboxes = new CheckBoxes( + [option.question.Clone()], + new UIEventSource([]), + "background-color: #F1F1F1;padding:0.25rem 0.5rem;", + "border:none;padding-left:3rem;color:#007759;display:flex;margin:0;justify-content:center;align-items:center;flex-direction:row;flex-wrap:nowrap;", + "margin:0;padding:0;", + "margin:0;padding:0.25rem 0 0 0.25rem;" + ); return new InputElementMap( checkboxes, @@ -111,7 +113,11 @@ export default class FilterView extends VariableUiElement { options.map( (option) => new FixedInputElement(option.question.Clone(), option.osmTags) - ) + ), + true, + "background-color: #F1F1F1;padding:0.25rem 0.5rem;", + "border:none;padding-left:3rem;color:#007759;display:flex;margin:0;justify-content:center;align-items:center;flex-direction:row;flex-wrap:nowrap;", + "margin:0;padding:0;" ); } } diff --git a/UI/Input/Checkboxes.ts b/UI/Input/Checkboxes.ts index e88d67633..18fccbea8 100644 --- a/UI/Input/Checkboxes.ts +++ b/UI/Input/Checkboxes.ts @@ -1,96 +1,110 @@ -import {InputElement} from "./InputElement"; -import {UIEventSource} from "../../Logic/UIEventSource"; -import {Utils} from "../../Utils"; +import { InputElement } from "./InputElement"; +import { UIEventSource } from "../../Logic/UIEventSource"; +import { Utils } from "../../Utils"; import BaseUIElement from "../BaseUIElement"; /** * Supports multi-input */ export default class CheckBoxes extends InputElement { - private static _nextId = 0; - IsSelected: UIEventSource = new UIEventSource(false); - private readonly value: UIEventSource - private readonly _elements: BaseUIElement[]; + private static _nextId = 0; + IsSelected: UIEventSource = new UIEventSource(false); + private readonly value: UIEventSource; + private readonly _elements: BaseUIElement[]; + private styleWrapperOverride = ""; + private styleInputOverride = ""; + private styleLabelOverride = ""; - constructor(elements: BaseUIElement[], value = new UIEventSource([])) { - super(); - this.value = value; - this._elements = Utils.NoNull(elements); - this.SetClass("flex flex-col") + constructor( + elements: BaseUIElement[], + value = new UIEventSource([]), + styleFormOverride = "", + styleWrapperOverride = "", + styleInputOverride = "", + styleLabelOverride = "" + ) { + super(); + this.value = value; + this._elements = Utils.NoNull(elements); + this.SetClass("flex flex-col"); + this.SetStyle(styleFormOverride); + this.styleWrapperOverride = styleWrapperOverride; + this.styleInputOverride = styleInputOverride; + this.styleLabelOverride = styleLabelOverride; + } - } + IsValid(ts: number[]): boolean { + return ts !== undefined; + } - IsValid(ts: number[]): boolean { - return ts !== undefined; + GetValue(): UIEventSource { + return this.value; + } - } + protected InnerConstructElement(): HTMLElement { + const el = document.createElement("form"); - GetValue(): UIEventSource { - return this.value; - } + const value = this.value; + const elements = this._elements; - protected InnerConstructElement(): HTMLElement { - const el = document.createElement("form") + for (let i = 0; i < elements.length; i++) { + let inputI = elements[i]; + const input = document.createElement("input"); + const id = CheckBoxes._nextId; + CheckBoxes._nextId++; + input.id = "checkbox" + id; - const value = this.value; - const elements = this._elements; + input.type = "checkbox"; + input.classList.add("p-1", "cursor-pointer", "m-3", "pl-3", "mr-0"); + input.style.cssText = this.styleInputOverride; - for (let i = 0; i < elements.length; i++) { - - let inputI = elements[i]; - const input = document.createElement("input") - const id = CheckBoxes._nextId - CheckBoxes._nextId++; - input.id = "checkbox" + id - - input.type = "checkbox" - input.classList.add("p-1","cursor-pointer","m-3","pl-3","mr-0") - - const label = document.createElement("label") - label.htmlFor = input.id - label.appendChild(inputI.ConstructElement()) - label.classList.add("block","w-full","p-2","cursor-pointer","bg-red") - - const wrapper = document.createElement("span") - wrapper.classList.add("flex","w-full","border", "border-gray-400","m-1") - wrapper.appendChild(input) - wrapper.appendChild(label) - el.appendChild(wrapper) - - value.addCallbackAndRunD(selectedValues => { - if (selectedValues.indexOf(i) >= 0) { - input.checked = true; - } - - - if(input.checked){ - wrapper.classList.remove("border-gray-400") - wrapper.classList.add("border-black") - }else{ - wrapper.classList.add("border-gray-400") - wrapper.classList.remove("border-black") - } - - }) - - input.onchange = () => { - // Index = index in the list of already checked items - const index = value.data.indexOf(i); - if (input.checked && index < 0) { - value.data.push(i); - value.ping(); - } else if (index >= 0) { - value.data.splice(index, 1); - value.ping(); - } - } + const label = document.createElement("label"); + label.htmlFor = input.id; + label.appendChild(inputI.ConstructElement()); + label.classList.add("block", "w-full", "p-2", "cursor-pointer", "bg-red"); + label.style.cssText = this.styleLabelOverride; + const wrapper = document.createElement("span"); + wrapper.classList.add( + "wrapper", + "flex", + "w-full", + "border", + "border-gray-400", + "m-1" + ); + wrapper.style.cssText = this.styleWrapperOverride; + wrapper.appendChild(input); + wrapper.appendChild(label); + el.appendChild(wrapper); + value.addCallbackAndRunD((selectedValues) => { + if (selectedValues.indexOf(i) >= 0) { + input.checked = true; } + if (input.checked) { + wrapper.classList.remove("border-gray-400"); + wrapper.classList.add("border-black"); + } else { + wrapper.classList.add("border-gray-400"); + wrapper.classList.remove("border-black"); + } + }); - return el; + input.onchange = () => { + // Index = index in the list of already checked items + const index = value.data.indexOf(i); + if (input.checked && index < 0) { + value.data.push(i); + value.ping(); + } else if (index >= 0) { + value.data.splice(index, 1); + value.ping(); + } + }; } - -} \ No newline at end of file + return el; + } +} diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index 2822b2166..c24c76800 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -1,157 +1,179 @@ -import {InputElement} from "./InputElement"; -import {UIEventSource} from "../../Logic/UIEventSource"; -import {Utils} from "../../Utils"; +import { InputElement } from "./InputElement"; +import { UIEventSource } from "../../Logic/UIEventSource"; +import { Utils } from "../../Utils"; export class RadioButton extends InputElement { - private static _nextId = 0; - IsSelected: UIEventSource = new UIEventSource(false); - private readonly value: UIEventSource; - private _elements: InputElement[]; - private _selectFirstAsDefault: boolean; + private static _nextId = 0; + IsSelected: UIEventSource = new UIEventSource(false); + private readonly value: UIEventSource; + private _elements: InputElement[]; + private _selectFirstAsDefault: boolean; + private styleFormOverride = ""; + private styleBlockOverride = ""; + private styleInputOverride = ""; + private styleLabelOverride = ""; - constructor(elements: InputElement[], - selectFirstAsDefault = true) { - super() - this._selectFirstAsDefault = selectFirstAsDefault; - this._elements = Utils.NoNull(elements); - this.value = new UIEventSource(undefined) - } - protected InnerConstructElement(): HTMLElement { - const elements = this._elements; - const selectFirstAsDefault = this._selectFirstAsDefault; - - const selectedElementIndex: UIEventSource = new UIEventSource(null); - const value = - UIEventSource.flatten(selectedElementIndex.map( - (selectedIndex) => { - if (selectedIndex !== undefined && selectedIndex !== null) { - return elements[selectedIndex].GetValue() - } - } - ), elements.map(e => e?.GetValue())); - value.syncWith(this.value) - - if(selectFirstAsDefault){ - - value.addCallbackAndRun(selected =>{ - if(selected === undefined){ - for (const element of elements) { - const v = element.GetValue().data; - if(v !== undefined){ - value.setData(v) - break; - } - } - - - } - }) + constructor( + elements: InputElement[], + selectFirstAsDefault = true, + styleFormOverride = "", + styleBlockOverride = "", + styleInputOverride = "", + styleLabelOverride = "" + ) { + super(); + this._selectFirstAsDefault = selectFirstAsDefault; + this._elements = Utils.NoNull(elements); + this.value = new UIEventSource(undefined); + this.styleFormOverride = styleFormOverride; + this.styleBlockOverride = styleBlockOverride; + this.styleInputOverride = styleInputOverride; + this.styleLabelOverride = styleLabelOverride; + } + protected InnerConstructElement(): HTMLElement { + const elements = this._elements; + const selectFirstAsDefault = this._selectFirstAsDefault; + const selectedElementIndex: UIEventSource = + new UIEventSource(null); + + const value = UIEventSource.flatten( + selectedElementIndex.map((selectedIndex) => { + if (selectedIndex !== undefined && selectedIndex !== null) { + return elements[selectedIndex].GetValue(); } + }), + elements.map((e) => e?.GetValue()) + ); + value.syncWith(this.value); - for (let i = 0; i < elements.length; i++) { - // If an element is clicked, the radio button corresponding with it should be selected as well - elements[i]?.onClick(() => { - selectedElementIndex.setData(i); - }); - elements[i].IsSelected.addCallback(isSelected => { - if (isSelected) { - selectedElementIndex.setData(i); - } - }) - elements[i].GetValue().addCallback(() => { - selectedElementIndex.setData(i); - }) + if (selectFirstAsDefault) { + value.addCallbackAndRun((selected) => { + if (selected === undefined) { + for (const element of elements) { + const v = element.GetValue().data; + if (v !== undefined) { + value.setData(v); + break; + } + } } - - - const groupId = "radiogroup" + RadioButton._nextId - RadioButton._nextId++ - - const form = document.createElement("form") - const inputs = [] - const wrappers: HTMLElement[] = [] - - for (let i1 = 0; i1 < elements.length; i1++) { - let element = elements[i1]; - const labelHtml = element.ConstructElement(); - if (labelHtml === undefined) { - continue; - } - - const input = document.createElement("input") - input.id = "radio" + groupId + "-" + i1; - input.name = groupId; - input.type = "radio" - input.classList.add("p-1","cursor-pointer","ml-2","pl-2","pr-0","m-3","mr-0") - - input.onchange = () => { - if(input.checked){ - selectedElementIndex.setData(i1) - } - } - - - inputs.push(input) - - const label = document.createElement("label") - label.appendChild(labelHtml) - label.htmlFor = input.id; - label.classList.add("block","w-full","p-2","cursor-pointer","bg-red") - - - const block = document.createElement("div") - block.appendChild(input) - block.appendChild(label) - block.classList.add("flex","w-full","border", "rounded-3xl", "border-gray-400","m-1") - wrappers.push(block) - - form.appendChild(block) - } - - - value.addCallbackAndRun( - selected => { - - let somethingChecked = false; - for (let i = 0; i < inputs.length; i++){ - let input = inputs[i]; - input.checked = !somethingChecked && elements[i].IsValid(selected); - somethingChecked = somethingChecked || input.checked - - if(input.checked){ - wrappers[i].classList.remove("border-gray-400") - wrappers[i].classList.add("border-black") - }else{ - wrappers[i].classList.add("border-gray-400") - wrappers[i].classList.remove("border-black") - } - - } - } - ) - - - this.SetClass("flex flex-col") - return form; + }); } - IsValid(t: T): boolean { - for (const inputElement of this._elements) { - if (inputElement.IsValid(t)) { - return true; - } + for (let i = 0; i < elements.length; i++) { + // If an element is clicked, the radio button corresponding with it should be selected as well + elements[i]?.onClick(() => { + selectedElementIndex.setData(i); + }); + elements[i].IsSelected.addCallback((isSelected) => { + if (isSelected) { + selectedElementIndex.setData(i); } - return false; + }); + elements[i].GetValue().addCallback(() => { + selectedElementIndex.setData(i); + }); } - GetValue(): UIEventSource { - return this.value; + const groupId = "radiogroup" + RadioButton._nextId; + RadioButton._nextId++; + + const form = document.createElement("form"); + form.style.cssText = this.styleFormOverride; + + const inputs = []; + const wrappers: HTMLElement[] = []; + + for (let i1 = 0; i1 < elements.length; i1++) { + let element = elements[i1]; + const labelHtml = element.ConstructElement(); + if (labelHtml === undefined) { + continue; + } + + const input = document.createElement("input"); + input.id = "radio" + groupId + "-" + i1; + input.name = groupId; + input.type = "radio"; + input.classList.add( + "p-1", + "cursor-pointer", + "ml-2", + "pl-2", + "pr-0", + "m-3", + "mr-0" + ); + input.style.cssText = this.styleInputOverride; + + input.onchange = () => { + if (input.checked) { + selectedElementIndex.setData(i1); + } + }; + + inputs.push(input); + + const label = document.createElement("label"); + label.appendChild(labelHtml); + label.htmlFor = input.id; + label.classList.add("block", "w-full", "p-2", "cursor-pointer", "bg-red"); + label.style.cssText = this.styleLabelOverride; + + const block = document.createElement("div"); + block.appendChild(input); + block.appendChild(label); + block.classList.add( + "flex", + "w-full", + "border", + "rounded-3xl", + "border-gray-400", + "m-1" + ); + block.style.cssText = this.styleBlockOverride; + wrappers.push(block); + + form.appendChild(block); } + value.addCallbackAndRun((selected) => { + let somethingChecked = false; + for (let i = 0; i < inputs.length; i++) { + let input = inputs[i]; + input.checked = !somethingChecked && elements[i].IsValid(selected); + somethingChecked = somethingChecked || input.checked; + if (input.checked) { + wrappers[i].classList.remove("border-gray-400"); + wrappers[i].classList.add("border-black"); + } else { + wrappers[i].classList.add("border-gray-400"); + wrappers[i].classList.remove("border-black"); + } + } + }); - /* + this.SetClass("flex flex-col"); + + return form; + } + + IsValid(t: T): boolean { + for (const inputElement of this._elements) { + if (inputElement.IsValid(t)) { + return true; + } + } + return false; + } + + GetValue(): UIEventSource { + return this.value; + } + + /* public ShowValue(t: T): boolean { if (t === undefined) { return false; @@ -173,6 +195,4 @@ export class RadioButton extends InputElement { } }*/ - - -} \ No newline at end of file +} diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index 19d267640..edad73927 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -421,6 +421,34 @@ "osmTags": "books~.*children.*" } ] + }, + { + "options": [ + { + "question": "Boeken voor volwassenen aanwezig?", + "osmTags": "books~.*adults.*" + } + ] + }, + { + "options": [ + { + "question": "Binnen of buiten", + "osmTags": { + "and": [] + } + }, + { + "question": "Binnen?", + "osmTags": "indoor=yes" + }, + { + "question": "Buiten?", + "osmTags": { + "or": ["indoor=no", "indoor="] + } + } + ] } ] }