Streamline download buttons

This commit is contained in:
pietervdvn 2021-07-28 16:48:59 +02:00
parent 0c760c8458
commit b5eb569802
10 changed files with 222 additions and 193 deletions

View file

@ -10,7 +10,6 @@ import SimpleAddUI from "./UI/BigComponents/SimpleAddUI";
import CenterMessageBox from "./UI/CenterMessageBox";
import UserBadge from "./UI/BigComponents/UserBadge";
import SearchAndGo from "./UI/BigComponents/SearchAndGo";
import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {Utils} from "./Utils";
import Svg from "./Svg";
@ -23,26 +22,22 @@ import UserDetails from "./Logic/Osm/OsmConnection";
import Attribution from "./UI/BigComponents/Attribution";
import LayerResetter from "./Logic/Actors/LayerResetter";
import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs";
import LayerControlPanel from "./UI/BigComponents/LayerControlPanel";
import ShowDataLayer from "./UI/ShowDataLayer";
import Hash from "./Logic/Web/Hash";
import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
import ScrollableFullScreen from "./UI/Base/ScrollableFullScreen";
import Translations from "./UI/i18n/Translations";
import MapControlButton from "./UI/MapControlButton";
import Combine from "./UI/Base/Combine";
import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler";
import LZString from "lz-string";
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
import AttributionPanel from "./UI/BigComponents/AttributionPanel";
import ContributorCount from "./Logic/ContributorCount";
import FeatureSource from "./Logic/FeatureSource/FeatureSource";
import AllKnownLayers from "./Customizations/AllKnownLayers";
import LayerConfig from "./Customizations/JSON/LayerConfig";
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
import {TagsFilter} from "./Logic/Tags/TagsFilter";
import FilterView from "./UI/BigComponents/FilterView";
import ExportPDF from "./UI/ExportPDF";
import LeftControls from "./UI/BigComponents/LeftControls";
import RightControls from "./UI/BigComponents/RightControls";
export class InitUiElements {
static InitAll(
@ -191,42 +186,11 @@ export class InitUiElements {
marker.addTo(State.state.leafletMap.data);
});
const geolocationButton = new Toggle(
new MapControlButton(
new GeoLocationHandler(
State.state.currentGPSLocation,
State.state.leafletMap,
State.state.layoutToUse
), {
dontStyle: true
}
),
undefined,
State.state.featureSwitchGeolocation
);
const plus = new MapControlButton(
Svg.plus_zoom_svg()
).onClick(() => {
State.state.locationControl.data.zoom++;
State.state.locationControl.ping();
});
const min = new MapControlButton(
Svg.min_zoom_svg()
).onClick(() => {
State.state.locationControl.data.zoom--;
State.state.locationControl.ping();
});
new Combine([plus, min, geolocationButton].map(el => el.SetClass("m-0.5 md:m-1")))
.SetClass("flex flex-col")
.AttachTo("bottom-right");
if (layoutToUse.id === personal.id) {
updateFavs();
}
InitUiElements.setupAllLayerElements();
if (layoutToUse.id === personal.id) {
@ -331,105 +295,6 @@ export class InitUiElements {
);
}
private static InitLayerSelection(featureSource: FeatureSource) {
const copyrightNotice = new ScrollableFullScreen(
() => Translations.t.general.attribution.attributionTitle.Clone(),
() =>
new AttributionPanel(
State.state.layoutToUse,
new ContributorCount(featureSource).Contributors
),
"copyright"
);
const copyrightButton = new Toggle(
copyrightNotice,
new MapControlButton(Svg.copyright_svg()),
copyrightNotice.isShown
)
.ToggleOnClick()
.SetClass("p-0.5");
const layerControlPanel = new LayerControlPanel(
State.state.layerControlIsOpened
).SetClass("block p-1 rounded-full");
const layerControlButton = new Toggle(
layerControlPanel,
new MapControlButton(Svg.layers_svg()),
State.state.layerControlIsOpened
).ToggleOnClick();
const layerControl = new Toggle(
layerControlButton,
"",
State.state.featureSwitchLayers
);
const filterView =
new ScrollableFullScreen(
() => Translations.t.general.layerSelection.title.Clone(),
() =>
new FilterView(State.state.filteredLayers).SetClass(
"block p-1 rounded-full"
),
undefined,
State.state.filterIsOpened
);
const filterMapControlButton = new MapControlButton(
Svg.filter_svg()
);
const filterButton = new Toggle(
filterView,
filterMapControlButton,
State.state.filterIsOpened
).ToggleOnClick();
const filterControl = new Toggle(
filterButton,
undefined,
State.state.featureSwitchFilter
);
const screenshot =
new Toggle(
new MapControlButton(
Svg.download_svg(),
).onClick(() => {
// Will already export
new ExportPDF(
{
freeDivId: "belowmap",
background: State.state.backgroundLayer,
location: State.state.locationControl,
features: State.state.featurePipeline.features,
layout: State.state.layoutToUse,
}
);
}), undefined, State.state.featureSwitchExportAsPdf)
new Combine([filterControl, layerControl, screenshot, copyrightButton,])
.SetClass("flex flex-col")
.AttachTo("bottom-left");
State.state.locationControl.addCallback(() => {
// Close the layer selection when the map is moved
layerControlButton.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
});
State.state.selectedElement.addCallbackAndRunD((_) => {
layerControlButton.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
});
}
private static InitBaseMap() {
State.state.availableBackgroundLayers =
AvailableBaseLayers.AvailableLayersAt(State.state.locationControl);
@ -561,7 +426,9 @@ export class InitUiElements {
// ------------- Setup the layers -------------------------------
const source = InitUiElements.InitLayers();
InitUiElements.InitLayerSelection(source);
new LeftControls(source).AttachTo("bottom-left");
new RightControls().AttachTo("bottom-right");
// ------------------ Setup various other UI elements ------------

View file

@ -79,7 +79,7 @@ export default class State {
public readonly featureSwitchUserbadge: UIEventSource<boolean>;
public readonly featureSwitchSearch: UIEventSource<boolean>;
public readonly featureSwitchLayers: UIEventSource<boolean>;
public readonly featureSwitchBackgroundSlection: UIEventSource<boolean>;
public readonly featureSwitchAddNew: UIEventSource<boolean>;
public readonly featureSwitchWelcomeMessage: UIEventSource<boolean>;
public readonly featureSwitchIframe: UIEventSource<boolean>;
@ -125,11 +125,11 @@ export default class State {
public layoutDefinition: string;
public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>;
public layerControlIsOpened: UIEventSource<boolean> =
public downloadControlIsOpened: UIEventSource<boolean> =
QueryParameters.GetQueryParameter(
"layer-control-toggle",
"download-control-toggle",
"false",
"Whether or not the layer control is shown"
"Whether or not the download panel is shown"
).map<boolean>(
(str) => str !== "false",
[],
@ -249,11 +249,12 @@ export default class State {
(layoutToUse) => layoutToUse?.enableSearch ?? true,
"Disables/Enables the search bar"
);
this.featureSwitchLayers = featSw(
"fs-layers",
(layoutToUse) => layoutToUse?.enableLayers ?? true,
"Disables/Enables the layer control"
this.featureSwitchBackgroundSlection = featSw(
"fs-background",
(layoutToUse) => layoutToUse?.enableBackgroundLayerSelection ?? true,
"Disables/Enables the background layer control"
);
this.featureSwitchFilter = featSw(
"fs-filter",
(layoutToUse) => layoutToUse?.enableLayers ?? true,

View file

@ -0,0 +1,53 @@
import State from "../../State";
import Combine from "../Base/Combine";
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations";
import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
import Toggle from "../Input/Toggle";
import {DownloadPanel} from "./DownloadPanel";
import {SubtleButton} from "../Base/SubtleButton";
import Svg from "../../Svg";
import ExportPDF from "../ExportPDF";
export default class AllDownloads extends ScrollableFullScreen {
constructor(isShown: UIEventSource<boolean>) {
super(AllDownloads.GenTitle, AllDownloads.GeneratePanel, "layers", isShown);
}
private static GenTitle(): BaseUIElement {
return Translations.t.general.download.title
.Clone()
.SetClass("text-2xl break-words font-bold p-2");
}
private static GeneratePanel(): BaseUIElement {
const generatePdf = () => {
new ExportPDF(
{
freeDivId: "belowmap",
background: State.state.backgroundLayer,
location: State.state.locationControl,
features: State.state.featurePipeline.features,
layout: State.state.layoutToUse,
})
}
const pdf = new Toggle(
new SubtleButton(Svg.floppy_ui(), Translations.t.general.download.downloadAsPdf.Clone().SetClass("font-bold"),)
.onClick(generatePdf),
undefined,
State.state.featureSwitchExportAsPdf
)
const exportPanel = new Toggle(
new DownloadPanel(),
undefined,
State.state.featureSwitchEnableExport
)
return new Combine([pdf, exportPanel]).SetClass("flex flex-col");
}
}

View file

@ -15,6 +15,7 @@ import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
import State from "../../State";
import FilteredLayer from "../../Models/FilteredLayer";
import BackgroundSelector from "./BackgroundSelector";
/**
@ -23,9 +24,14 @@ import FilteredLayer from "../../Models/FilteredLayer";
export default class FilterView extends VariableUiElement {
constructor(filteredLayer: UIEventSource<FilteredLayer[]>) {
const backgroundSelector = new Toggle(
new BackgroundSelector(),
undefined,
State.state.featureSwitchBackgroundSlection
)
super(
filteredLayer.map((filteredLayers) =>
filteredLayers?.map(l => FilterView.createOneFilteredLayerElement(l))
filteredLayers?.map(l => FilterView.createOneFilteredLayerElement(l)).concat(backgroundSelector)
)
);
}

View file

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

View file

@ -0,0 +1,94 @@
import Combine from "../Base/Combine";
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations";
import AttributionPanel from "./AttributionPanel";
import State from "../../State";
import ContributorCount from "../../Logic/ContributorCount";
import Toggle from "../Input/Toggle";
import MapControlButton from "../MapControlButton";
import Svg from "../../Svg";
import AllDownloads from "./AllDownloads";
import FilterView from "./FilterView";
import FeatureSource from "../../Logic/FeatureSource/FeatureSource";
export default class LeftControls extends Combine {
constructor(featureSource: FeatureSource) {
const toggledCopyright = new ScrollableFullScreen(
() => Translations.t.general.attribution.attributionTitle.Clone(),
() =>
new AttributionPanel(
State.state.layoutToUse,
new ContributorCount(featureSource).Contributors
),
undefined
);
const copyrightButton = new Toggle(
toggledCopyright,
new MapControlButton(Svg.copyright_svg()),
toggledCopyright.isShown
)
.ToggleOnClick()
.SetClass("p-0.5");
const toggledDownload = new Toggle(
new AllDownloads(
State.state.downloadControlIsOpened
).SetClass("block p-1 rounded-full"),
new MapControlButton(Svg.download_svg()),
State.state.downloadControlIsOpened
).ToggleOnClick();
const downloadButtonn = new Toggle(
toggledDownload,
undefined,
State.state.featureSwitchEnableExport.map(downloadEnabled => downloadEnabled || State.state.featureSwitchExportAsPdf.data,
[State.state.featureSwitchExportAsPdf])
);
const toggledFilter = new Toggle(
new ScrollableFullScreen(
() => Translations.t.general.layerSelection.title.Clone(),
() =>
new FilterView(State.state.filteredLayers).SetClass(
"block p-1 rounded-full"
),
undefined,
State.state.filterIsOpened
),
new MapControlButton(Svg.filter_svg()),
State.state.filterIsOpened
).ToggleOnClick();
const filterButton = new Toggle(
toggledFilter,
undefined,
State.state.featureSwitchFilter
);
State.state.locationControl.addCallback(() => {
// Close the layer selection when the map is moved
toggledDownload.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
toggledFilter.isEnabled.setData(false);
});
State.state.selectedElement.addCallbackAndRunD((_) => {
toggledDownload.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
toggledFilter.isEnabled.setData(false);
});
super([filterButton,
downloadButtonn,
copyrightButton])
this.SetClass("flex flex-col")
}
}

View file

@ -0,0 +1,43 @@
import Combine from "../Base/Combine";
import Toggle from "../Input/Toggle";
import MapControlButton from "../MapControlButton";
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
import State from "../../State";
import Svg from "../../Svg";
export default class RightControls extends Combine {
constructor() {
const geolocationButton = new Toggle(
new MapControlButton(
new GeoLocationHandler(
State.state.currentGPSLocation,
State.state.leafletMap,
State.state.layoutToUse
), {
dontStyle: true
}
),
undefined,
State.state.featureSwitchGeolocation
);
const plus = new MapControlButton(
Svg.plus_zoom_svg()
).onClick(() => {
State.state.locationControl.data.zoom++;
State.state.locationControl.ping();
});
const min = new MapControlButton(
Svg.min_zoom_svg()
).onClick(() => {
State.state.locationControl.data.zoom--;
State.state.locationControl.ping();
});
super([plus, min, geolocationButton].map(el => el.SetClass("m-0.5 md:m-1")))
this.SetClass("flex flex-col")
}
}

View file

@ -166,7 +166,7 @@ export default class SimpleAddUI extends Toggle {
])
)
.onClick(() => State.state.layerControlIsOpened.setData(true))
.onClick(() => State.state.filterIsOpened.setData(true))
const openLayerOrConfirm = new Toggle(
confirmButton,
@ -238,7 +238,13 @@ export default class SimpleAddUI extends Toggle {
const allButtons = [];
for (const layer of State.state.filteredLayers.data) {
if (layer.isDisplayed.data === false && State.state.featureSwitchLayers) {
if (layer.isDisplayed.data === false && State.state.featureSwitchFilter.data) {
// The layer is not displayed and we cannot enable the layer control -> we skip
continue;
}
if(layer.layerDef.name === undefined){
// this is a parlty hidden layer
continue;
}

View file

@ -23,7 +23,7 @@
"startLat": 51,
"startLon": 3.75,
"startZoom": 11,
"widenFactor": 0.0,
"widenFactor": 0,
"socialImage": "./assets/themes/cycle_infra/cycle-infra.svg",
"enableDownload": true,
"layers": [

View file

@ -162,6 +162,7 @@
},
"download": {
"title": "Download visible data",
"downloadAsPdf": "Download a PDF of the current map",
"downloadGeojson": "Download visible data as geojson",
"downloadGeoJsonHelper": "Compatible with QGIS, OsmAnd, ArcGIS, ESRI, ...",
"downloadCSV": "Download as CSV",