start creating extra filter

This commit is contained in:
Ward 2021-07-26 12:26:41 +02:00
parent 97c85d6909
commit e9160504a6
7 changed files with 872 additions and 800 deletions

View file

@ -1,19 +1,19 @@
import {CenterFlexedElement} from "./UI/Base/CenterFlexedElement"; import { CenterFlexedElement } from "./UI/Base/CenterFlexedElement";
import {FixedUiElement} from "./UI/Base/FixedUiElement"; import { FixedUiElement } from "./UI/Base/FixedUiElement";
import Toggle from "./UI/Input/Toggle"; import Toggle from "./UI/Input/Toggle";
import {Basemap} from "./UI/BigComponents/Basemap"; import { Basemap } from "./UI/BigComponents/Basemap";
import State from "./State"; import State from "./State";
import LoadFromOverpass from "./Logic/Actors/OverpassFeatureSource"; import LoadFromOverpass from "./Logic/Actors/OverpassFeatureSource";
import {UIEventSource} from "./Logic/UIEventSource"; import { UIEventSource } from "./Logic/UIEventSource";
import {QueryParameters} from "./Logic/Web/QueryParameters"; import { QueryParameters } from "./Logic/Web/QueryParameters";
import StrayClickHandler from "./Logic/Actors/StrayClickHandler"; import StrayClickHandler from "./Logic/Actors/StrayClickHandler";
import SimpleAddUI from "./UI/BigComponents/SimpleAddUI"; import SimpleAddUI from "./UI/BigComponents/SimpleAddUI";
import CenterMessageBox from "./UI/CenterMessageBox"; import CenterMessageBox from "./UI/CenterMessageBox";
import UserBadge from "./UI/BigComponents/UserBadge"; import UserBadge from "./UI/BigComponents/UserBadge";
import SearchAndGo from "./UI/BigComponents/SearchAndGo"; import SearchAndGo from "./UI/BigComponents/SearchAndGo";
import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler"; import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; import { LocalStorageSource } from "./Logic/Web/LocalStorageSource";
import {Utils} from "./Utils"; import { Utils } from "./Utils";
import Svg from "./Svg"; import Svg from "./Svg";
import Link from "./UI/Base/Link"; import Link from "./UI/Base/Link";
import * as personal from "./assets/themes/personalLayout/personalLayout.json"; import * as personal from "./assets/themes/personalLayout/personalLayout.json";
@ -34,16 +34,17 @@ import MapControlButton from "./UI/MapControlButton";
import Combine from "./UI/Base/Combine"; import Combine from "./UI/Base/Combine";
import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler";
import LZString from "lz-string"; import LZString from "lz-string";
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson"; import { LayoutConfigJson } from "./Customizations/JSON/LayoutConfigJson";
import AttributionPanel from "./UI/BigComponents/AttributionPanel"; import AttributionPanel from "./UI/BigComponents/AttributionPanel";
import ContributorCount from "./Logic/ContributorCount"; import ContributorCount from "./Logic/ContributorCount";
import FeatureSource from "./Logic/FeatureSource/FeatureSource"; import FeatureSource from "./Logic/FeatureSource/FeatureSource";
import AllKnownLayers from "./Customizations/AllKnownLayers"; import AllKnownLayers from "./Customizations/AllKnownLayers";
import LayerConfig from "./Customizations/JSON/LayerConfig"; import LayerConfig from "./Customizations/JSON/LayerConfig";
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; import { SimpleMapScreenshoter } from "leaflet-simple-map-screenshoter";
import jsPDF from "jspdf"; import jsPDF from "jspdf";
import FilterView from "./UI/BigComponents/FilterView"; import FilterView from "./UI/BigComponents/FilterView";
import { TagsFilter } from "./Logic/Tags/TagsFilter";
export class InitUiElements { export class InitUiElements {
static InitAll( static InitAll(
@ -59,8 +60,7 @@ export class InitUiElements {
`Error: incorrect layout <i>${layoutName}</i><br/><a href='https://${window.location.host}/'>Go back</a>` `Error: incorrect layout <i>${layoutName}</i><br/><a href='https://${window.location.host}/'>Go back</a>`
) )
.AttachTo("centermessage") .AttachTo("centermessage")
.onClick(() => { .onClick(() => {});
});
throw "Incorrect layout"; throw "Incorrect layout";
} }
@ -188,7 +188,7 @@ export class InitUiElements {
iconSize: [30, 30], iconSize: [30, 30],
iconAnchor: [15, 15], iconAnchor: [15, 15],
}); });
const marker = L.marker([home.lat, home.lon], {icon: icon}); const marker = L.marker([home.lat, home.lon], { icon: icon });
marker.addTo(State.state.leafletMap.data); marker.addTo(State.state.leafletMap.data);
}); });
@ -222,7 +222,6 @@ export class InitUiElements {
State.state.locationControl.ping(); State.state.locationControl.ping();
}); });
// To download pdf of leaflet you need to turn it into and image first // To download pdf of leaflet you need to turn it into and image first
// Then export that image as a pdf // Then export that image as a pdf
// leaflet-simple-map-screenshoter: to make image // leaflet-simple-map-screenshoter: to make image
@ -237,10 +236,10 @@ export class InitUiElements {
console.log("Debug - Screenshot"); console.log("Debug - Screenshot");
screenshotter.addTo(State.state.leafletMap.data); screenshotter.addTo(State.state.leafletMap.data);
let doc = new jsPDF(); let doc = new jsPDF();
screenshotter.takeScreen('image').then(image => { screenshotter.takeScreen("image").then((image) => {
// TO DO: scale image on pdf to its original size // TO DO: scale image on pdf to its original size
doc.addImage(image, 'PNG', 0, 0, screen.width / 10, screen.height / 10); doc.addImage(image, "PNG", 0, 0, screen.width / 10, screen.height / 10);
doc.setDisplayMode('fullheight'); doc.setDisplayMode("fullheight");
doc.save("Screenshot"); doc.save("Screenshot");
}); });
//screenshotter.remove(); //screenshotter.remove();
@ -249,7 +248,9 @@ export class InitUiElements {
}); });
new Combine( new Combine(
[plus, min, geolocationButton, screenshot].map((el) => el.SetClass("m-0.5 md:m-1")) [plus, min, geolocationButton, screenshot].map((el) =>
el.SetClass("m-0.5 md:m-1")
)
) )
.SetClass("flex flex-col") .SetClass("flex flex-col")
.AttachTo("bottom-right"); .AttachTo("bottom-right");
@ -396,31 +397,29 @@ export class InitUiElements {
State.state.featureSwitchLayers State.state.featureSwitchLayers
); );
const filterView = new FilterView(State.state.FilterIsOpened).SetClass( // const filterView = new FilterView(State.state.FilterIsOpened).SetClass(
"block p-1 rounded-full" // "block p-1 rounded-full"
); // );
const filterMapControlButton = new MapControlButton( // const filterMapControlButton = new MapControlButton(
new CenterFlexedElement( // new CenterFlexedElement(
Img.AsImageElement(Svg.filter, "", "width:1.25rem;height:1.25rem") // Img.AsImageElement(Svg.filter, "", "width:1.25rem;height:1.25rem")
) // )
); // );
const filterButton = new Toggle( // const filterButton = new Toggle(
filterView, // filterView,
filterMapControlButton, // filterMapControlButton,
State.state.FilterIsOpened // State.state.FilterIsOpened
).ToggleOnClick(); // ).ToggleOnClick();
const filterControl = new Toggle( // const filterControl = new Toggle(
filterButton, // filterButton,
"", // "",
State.state.featureSwitchFilter // State.state.featureSwitchFilter
); // );
new Combine([copyrightButton, layerControl, filterControl]).AttachTo( new Combine([copyrightButton, layerControl]).AttachTo("bottom-left");
"bottom-left"
);
State.state.locationControl.addCallback(() => { State.state.locationControl.addCallback(() => {
// Close the layer selection when the map is moved // Close the layer selection when the map is moved
@ -435,7 +434,8 @@ export class InitUiElements {
} }
private static InitBaseMap() { private static InitBaseMap() {
State.state.availableBackgroundLayers = AvailableBaseLayers.AvailableLayersAt(State.state.locationControl); State.state.availableBackgroundLayers =
AvailableBaseLayers.AvailableLayersAt(State.state.locationControl);
State.state.backgroundLayer = State.state.backgroundLayerId.map( State.state.backgroundLayer = State.state.backgroundLayerId.map(
(selectedId: string) => { (selectedId: string) => {
if (selectedId === undefined) { if (selectedId === undefined) {
@ -519,6 +519,7 @@ export class InitUiElements {
const flayer = { const flayer = {
isDisplayed: isDisplayed, isDisplayed: isDisplayed,
layerDef: layer, layerDef: layer,
appliedFilters: new UIEventSource<TagsFilter>(undefined)
}; };
flayers.push(flayer); flayers.push(flayer);
} }

View file

@ -1,66 +1,87 @@
import FeatureSource from "./FeatureSource"; import FeatureSource from "./FeatureSource";
import {UIEventSource} from "../UIEventSource"; import { UIEventSource } from "../UIEventSource";
import LayerConfig from "../../Customizations/JSON/LayerConfig"; import LayerConfig from "../../Customizations/JSON/LayerConfig";
import Loc from "../../Models/Loc"; import Loc from "../../Models/Loc";
import Hash from "../Web/Hash"; import Hash from "../Web/Hash";
import { TagsFilter } from "../Tags/TagsFilter";
export default class FilteringFeatureSource implements FeatureSource { export default class FilteringFeatureSource implements FeatureSource {
public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); public features: UIEventSource<{ feature: any; freshness: Date }[]> =
public readonly name = "FilteringFeatureSource" new UIEventSource<{ feature: any; freshness: Date }[]>([]);
public readonly name = "FilteringFeatureSource";
constructor(layers: UIEventSource<{ constructor(
isDisplayed: UIEventSource<boolean>, layers: UIEventSource<
layerDef: LayerConfig {
}[]>, isDisplayed: UIEventSource<boolean>;
layerDef: LayerConfig;
appliedFilters: UIEventSource<TagsFilter>;
}[]
>,
location: UIEventSource<Loc>, location: UIEventSource<Loc>,
selectedElement: UIEventSource<any>, selectedElement: UIEventSource<any>,
upstream: FeatureSource) { upstream: FeatureSource
) {
const self = this; const self = this;
function update() { function update() {
const layerDict = {}; const layerDict = {};
if (layers.data.length == 0) { if (layers.data.length == 0) {
console.warn("No layers defined!") console.warn("No layers defined!");
return; return;
} }
for (const layer of layers.data) { for (const layer of layers.data) {
layerDict[layer.layerDef.id] = layer; layerDict[layer.layerDef.id] = layer;
} }
const features: { feature: any, freshness: Date }[] = upstream.features.data; const features: { feature: any; freshness: Date }[] =
upstream.features.data;
const missingLayers = new Set<string>(); const missingLayers = new Set<string>();
const newFeatures = features.filter(f => { const newFeatures = features.filter((f) => {
const layerId = f.feature._matching_layer_id; const layerId = f.feature._matching_layer_id;
if(selectedElement.data?.id === f.feature.id || f.feature.id === Hash.hash.data){ if (
selectedElement.data?.id === f.feature.id ||
f.feature.id === Hash.hash.data
) {
// This is the selected object - it gets a free pass even if zoom is not sufficient // This is the selected object - it gets a free pass even if zoom is not sufficient
return true; return true;
} }
if (layerId !== undefined) { if (layerId !== undefined) {
const layer: { const layer: {
isDisplayed: UIEventSource<boolean>, isDisplayed: UIEventSource<boolean>;
layerDef: LayerConfig layerDef: LayerConfig;
appliedFilters: UIEventSource<TagsFilter>;
} = layerDict[layerId]; } = layerDict[layerId];
if (layer === undefined) { if (layer === undefined) {
missingLayers.add(layerId) missingLayers.add(layerId);
return true; return true;
} }
const isShown = layer.layerDef.isShown const isShown = layer.layerDef.isShown;
const tags = f.feature.properties; const tags = f.feature.properties;
if (isShown.IsKnown(tags)) { if (isShown.IsKnown(tags)) {
const result = layer.layerDef.isShown.GetRenderValue(f.feature.properties).txt; const result = layer.layerDef.isShown.GetRenderValue(
f.feature.properties
).txt;
if (result !== "yes") { if (result !== "yes") {
return false; return false;
} }
} }
if (FilteringFeatureSource.showLayer(layer, location)) { if (FilteringFeatureSource.showLayer(layer, location)) {
const tagsFilter = layer.appliedFilters.data;
if (tagsFilter) {
const properties = f.feature.properties;
if (!tagsFilter.matchesProperties(properties)) {
return false;
}
}
return true; return true;
} }
} }
@ -69,25 +90,36 @@ export default class FilteringFeatureSource implements FeatureSource {
if (!FilteringFeatureSource.showLayer(toCheck, location)) { if (!FilteringFeatureSource.showLayer(toCheck, location)) {
continue; continue;
} }
if (toCheck.layerDef.source.osmTags.matchesProperties(f.feature.properties)) { if (
toCheck.layerDef.source.osmTags.matchesProperties(
f.feature.properties
)
) {
return true; return true;
} }
} }
return false; return false;
}); });
console.log("Filtering layer source: input: ", upstream.features.data?.length, "output:", newFeatures.length) console.log(
"Filtering layer source: input: ",
upstream.features.data?.length,
"output:",
newFeatures.length
);
self.features.setData(newFeatures); self.features.setData(newFeatures);
if (missingLayers.size > 0) { if (missingLayers.size > 0) {
console.error("Some layers were not found: ", Array.from(missingLayers)) console.error(
"Some layers were not found: ",
Array.from(missingLayers)
);
} }
} }
upstream.features.addCallback(() => { upstream.features.addCallback(() => {
update() update();
}); });
location.map(l => { location
.map((l) => {
// We want something that is stable for the shown layers // We want something that is stable for the shown layers
const displayedLayerIndexes = []; const displayedLayerIndexes = [];
for (let i = 0; i < layers.data.length; i++) { for (let i = 0; i < layers.data.length; i++) {
@ -103,32 +135,40 @@ export default class FilteringFeatureSource implements FeatureSource {
} }
displayedLayerIndexes.push(i); displayedLayerIndexes.push(i);
} }
return displayedLayerIndexes.join(",") return displayedLayerIndexes.join(",");
}).addCallback(() => { })
.addCallback(() => {
update(); update();
}); });
layers.addCallback(update); layers.addCallback(update);
const registered = new Set<UIEventSource<boolean>>(); const registered = new Set<UIEventSource<boolean>>();
layers.addCallbackAndRun(layers => { layers.addCallbackAndRun((layers) => {
for (const layer of layers) { for (const layer of layers) {
if (registered.has(layer.isDisplayed)) { if (registered.has(layer.isDisplayed)) {
continue; continue;
} }
registered.add(layer.isDisplayed); registered.add(layer.isDisplayed);
layer.isDisplayed.addCallback(() => update()); layer.isDisplayed.addCallback(() => update());
layer.appliedFilters.addCallback(() => update());
} }
}) });
update(); update();
} }
private static showLayer(layer: { private static showLayer(
isDisplayed: UIEventSource<boolean>, layer: {
layerDef: LayerConfig isDisplayed: UIEventSource<boolean>;
}, location: UIEventSource<Loc>) { layerDef: LayerConfig;
return layer.isDisplayed.data && (layer.layerDef.minzoom <= location.data.zoom) && (layer.layerDef.maxzoom >= location.data.zoom) },
location: UIEventSource<Loc>
) {
return (
layer.isDisplayed.data &&
layer.layerDef.minzoom <= location.data.zoom &&
layer.layerDef.maxzoom >= location.data.zoom
);
} }
} }

View file

@ -20,6 +20,7 @@ import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader";
import {Relation} from "./Logic/Osm/ExtractRelations"; import {Relation} from "./Logic/Osm/ExtractRelations";
import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource";
import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
import { TagsFilter } from "./Logic/Tags/TagsFilter";
/** /**
* Contains the global state: a bunch of UI-event sources * Contains the global state: a bunch of UI-event sources
@ -62,9 +63,11 @@ export default class State {
public filteredLayers: UIEventSource<{ public filteredLayers: UIEventSource<{
readonly isDisplayed: UIEventSource<boolean>; readonly isDisplayed: UIEventSource<boolean>;
readonly appliedFilters: UIEventSource<TagsFilter>;
readonly layerDef: LayerConfig; readonly layerDef: LayerConfig;
}[]> = new UIEventSource<{ }[]> = new UIEventSource<{
readonly isDisplayed: UIEventSource<boolean>; readonly isDisplayed: UIEventSource<boolean>;
readonly appliedFilters: UIEventSource<TagsFilter>;
readonly layerDef: LayerConfig; readonly layerDef: LayerConfig;
}[]>([]); }[]>([]);

View file

@ -1,46 +1,43 @@
import {UIEventSource} from "../../Logic/UIEventSource"; import { UIEventSource } from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement"; import BaseUIElement from "../BaseUIElement";
export class VariableUiElement extends BaseUIElement { export class VariableUiElement extends BaseUIElement {
private _element: HTMLElement;
private _element : HTMLElement; constructor(
contents: UIEventSource<string | BaseUIElement | BaseUIElement[]>
constructor(contents: UIEventSource<string | BaseUIElement | BaseUIElement[]>) { ) {
super(); super();
this._element = document.createElement("span") this._element = document.createElement("span");
const el = this._element const el = this._element;
contents.addCallbackAndRun(contents => { contents.addCallbackAndRun((contents) => {
while (el.firstChild) { while (el.firstChild) {
el.removeChild( el.removeChild(el.lastChild);
el.lastChild
)
} }
if (contents === undefined) { if (contents === undefined) {
return el; return el;
} }
if (typeof contents === "string") { if (typeof contents === "string") {
el.innerHTML = contents el.innerHTML = contents;
} else if (contents instanceof Array) { } else if (contents instanceof Array) {
for (const content of contents) { for (const content of contents) {
const c = content.ConstructElement(); const c = content.ConstructElement();
if (c !== undefined && c !== null) { if (c !== undefined && c !== null) {
el.appendChild(c) el.appendChild(c);
} }
} }
} else { } else {
const c = contents.ConstructElement(); const c = contents.ConstructElement();
if (c !== undefined && c !== null) { if (c !== undefined && c !== null) {
el.appendChild(c) el.appendChild(c);
} }
} }
}) });
} }
protected InnerConstructElement(): HTMLElement { protected InnerConstructElement(): HTMLElement {
return this._element; return this._element;
} }
} }

View file

@ -1,3 +1,6 @@
import { Utils } from "./../../Utils";
import { FixedInputElement } from "./../Input/FixedInputElement";
import { RadioButton } from "./../Input/RadioButton";
import { FixedUiElement } from "./../Base/FixedUiElement"; import { FixedUiElement } from "./../Base/FixedUiElement";
import { LayerConfigJson } from "./../../Customizations/JSON/LayerConfigJson"; import { LayerConfigJson } from "./../../Customizations/JSON/LayerConfigJson";
import { UIEventSource } from "../../Logic/UIEventSource"; import { UIEventSource } from "../../Logic/UIEventSource";
@ -11,34 +14,28 @@ import BaseUIElement from "../BaseUIElement";
import { Translation } from "../i18n/Translation"; import { Translation } from "../i18n/Translation";
import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Svg from "../../Svg"; import Svg from "../../Svg";
import FilterConfig from "../../Customizations/JSON/FilterConfig";
import CheckBoxes from "../Input/Checkboxes";
import { InputElement } from "../Input/InputElement";
import { TagsFilter } from "../../Logic/Tags/TagsFilter";
import InputElementMap from "../Input/InputElementMap";
import { And } from "../../Logic/Tags/And";
/** /**
* Shows the filter * Shows the filter
*/ */
export default class FilterView extends ScrollableFullScreen {
constructor(isShown: UIEventSource<boolean>) {
super(FilterView.GenTitle, FilterView.Generatecontent, "filter", isShown);
}
private static GenTitle(): BaseUIElement { export default class FilterView extends VariableUiElement {
return new FixedUiElement(`Filter`).SetClass( constructor(filteredLayer) {
"text-2xl break-words font-bold p-2" super(
filteredLayer.map((filteredLayers) =>
filteredLayers.map(FilterView.createOneFilteredLayerElement)
)
); );
} }
private static Generatecontent(): BaseUIElement { static createOneFilteredLayerElement(filteredLayer) {
let filterPanel: BaseUIElement = new FixedUiElement(""); const layer: LayerConfig = filteredLayer.layerDef;
if (State.state.filteredLayers.data.length > 1) {
let activeLayers = State.state.filteredLayers;
if (activeLayers === undefined) {
throw "ActiveLayers should be defined...";
}
const checkboxes: BaseUIElement[] = [];
for (const layer of activeLayers.data) {
const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem"; const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem";
const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle); const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle);
@ -46,13 +43,15 @@ export default class FilterView extends ScrollableFullScreen {
iconStyle iconStyle
); );
if (layer.layerDef.name === undefined) { if (filteredLayer.layerDef.name === undefined) {
continue; return;
} }
const style = "display:flex;align-items:center;color:#007759"; const style = "display:flex;align-items:center;color:#007759";
const name: Translation = Translations.WT(layer.layerDef.name)?.Clone(); const name: Translation = Translations.WT(
filteredLayer.layerDef.name
)?.Clone();
const styledNameChecked = name const styledNameChecked = name
.Clone() .Clone()
@ -62,28 +61,57 @@ export default class FilterView extends ScrollableFullScreen {
.Clone() .Clone()
.SetStyle("font-size:large;padding-left:1.25rem"); .SetStyle("font-size:large;padding-left:1.25rem");
const layerChecked = new Combine([icon, styledNameChecked]).SetStyle( const layerChecked = new Combine([icon, styledNameChecked]).SetStyle(style);
style
);
const layerNotChecked = new Combine([ const layerNotChecked = new Combine([
iconUnselected, iconUnselected,
styledNameUnChecked, styledNameUnChecked,
]).SetStyle(style); ]).SetStyle(style);
checkboxes.push( let listFilterElements: InputElement<TagsFilter>[] = layer.filters.map(
new Toggle(layerChecked, layerNotChecked, layer.isDisplayed) FilterView.createFilter
);
function update() {
let listTagsFilters = Utils.NoNull(
listFilterElements.map((input) => input.GetValue().data)
);
filteredLayer.appliedTags.setData(new And(listTagsFilters));
}
listFilterElements.forEach((inputElement) =>
inputElement.GetValue().addCallback((_) => update())
);
return new Toggle(
new Combine([layerChecked, ...listFilterElements]),
layerNotChecked,
filteredLayer.isDisplayed
)
.ToggleOnClick() .ToggleOnClick()
.SetStyle("margin:0.3em;") .SetStyle("margin:0.3em;");
}
static createFilter(filterConfig: FilterConfig): InputElement<TagsFilter> {
if (filterConfig.options.length === 1) {
let option = filterConfig.options[0];
let checkboxes = new CheckBoxes([option.question.Clone()]);
return new InputElementMap(
checkboxes,
(t0, t1) => t0 === t1,
(numbers) => (numbers.length > 0 ? option.osmTags : undefined),
(tagsFilter) => (tagsFilter == undefined ? [] : [0])
); );
} }
let combinedCheckboxes = new Combine(checkboxes); let options = filterConfig.options;
combinedCheckboxes.SetStyle("display:flex;flex-direction:column;");
filterPanel = new Combine([combinedCheckboxes]); return new RadioButton(
options.map(
return filterPanel; (option) =>
} new FixedInputElement(option.question.Clone(), option.osmTags)
)
);
} }
} }

View file

@ -4,45 +4,56 @@ import LayerSelection from "./LayerSelection";
import Combine from "../Base/Combine"; import Combine from "../Base/Combine";
import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations"; import Translations from "../i18n/Translations";
import {UIEventSource} from "../../Logic/UIEventSource"; import { UIEventSource } from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement"; import BaseUIElement from "../BaseUIElement";
import Toggle from "../Input/Toggle"; import Toggle from "../Input/Toggle";
import {ExportDataButton} from "./ExportDataButton"; import { ExportDataButton } from "./ExportDataButton";
import FilterView from "./FilterView";
export default class LayerControlPanel extends ScrollableFullScreen { export default class LayerControlPanel extends ScrollableFullScreen {
constructor(isShown: UIEventSource<boolean>) { constructor(isShown: UIEventSource<boolean>) {
super(LayerControlPanel.GenTitle, LayerControlPanel.GeneratePanel, "layers", isShown); super(
LayerControlPanel.GenTitle,
LayerControlPanel.GeneratePanel,
"layers",
isShown
);
} }
private static GenTitle(): BaseUIElement { private static GenTitle(): BaseUIElement {
return Translations.t.general.layerSelection.title.Clone().SetClass("text-2xl break-words font-bold p-2") return Translations.t.general.layerSelection.title
.Clone()
.SetClass("text-2xl break-words font-bold p-2");
} }
private static GeneratePanel(): BaseUIElement { private static GeneratePanel(): BaseUIElement {
const elements: BaseUIElement[] = [] const elements: BaseUIElement[] = [];
if (State.state.layoutToUse.data.enableBackgroundLayerSelection) { if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
const backgroundSelector = new BackgroundSelector(); const backgroundSelector = new BackgroundSelector();
backgroundSelector.SetStyle("margin:1em"); backgroundSelector.SetStyle("margin:1em");
backgroundSelector.onClick(() => { backgroundSelector.onClick(() => {});
}); elements.push(backgroundSelector);
elements.push(backgroundSelector)
} }
elements.push(new Toggle( elements.push(
new LayerSelection(State.state.filteredLayers), new Toggle(
new FilterView(State.state.filteredLayers),
undefined, undefined,
State.state.filteredLayers.map(layers => layers.length > 1) State.state.filteredLayers.map(
)) (layers) => layers.length > 1 || layers[0].layerDef.filters.length > 0
)
)
);
elements.push(new Toggle( elements.push(
new Toggle(
new ExportDataButton(), new ExportDataButton(),
undefined, undefined,
State.state.featureSwitchEnableExport State.state.featureSwitchEnableExport
)) )
);
return new Combine(elements).SetClass("flex flex-col") return new Combine(elements).SetClass("flex flex-col");
} }
} }

View file

@ -71,9 +71,7 @@
"ru": "Книжный шкаф", "ru": "Книжный шкаф",
"it": "Microbiblioteca" "it": "Microbiblioteca"
}, },
"tags": [ "tags": ["amenity=public_bookcase"],
"amenity=public_bookcase"
],
"preciseInput": { "preciseInput": {
"preferredBackground": "photo" "preferredBackground": "photo"
} }
@ -107,10 +105,7 @@
"mappings": [ "mappings": [
{ {
"if": { "if": {
"and": [ "and": ["noname=yes", "name="]
"noname=yes",
"name="
]
}, },
"then": { "then": {
"en": "This bookcase doesn't have a name", "en": "This bookcase doesn't have a name",
@ -316,18 +311,12 @@
"it": "Fa parte della rete 'Little Free Library'" "it": "Fa parte della rete 'Little Free Library'"
}, },
"if": { "if": {
"and": [ "and": ["brand=Little Free Library", "nobrand="]
"brand=Little Free Library",
"nobrand="
]
} }
}, },
{ {
"if": { "if": {
"and": [ "and": ["nobrand=yes", "brand="]
"nobrand=yes",
"brand="
]
}, },
"then": { "then": {
"en": "This public bookcase is not part of a bigger network", "en": "This public bookcase is not part of a bigger network",
@ -368,11 +357,7 @@
"it": "Questa microbiblioteca non fa parte di una rete" "it": "Questa microbiblioteca non fa parte di una rete"
}, },
"if": { "if": {
"and": [ "and": ["nobrand=yes", "brand=", "ref="]
"nobrand=yes",
"brand=",
"ref="
]
} }
} }
] ]
@ -424,11 +409,18 @@
], ],
"deletion": { "deletion": {
"softDeletionTags": { "softDeletionTags": {
"and": [ "and": ["disused:amenity=public_bookcase", "amenity="]
"disused:amenity=public_bookcase",
"amenity="
]
}, },
"neededChangesets": 5 "neededChangesets": 5
},
"filter": [
{
"options": [
{
"question": "Kinderboeken aanwezig?",
"osmTags": "books~.*children.*"
} }
]
}
]
} }