import React from "react"; import { type WithTranslation, withTranslation } from "react-i18next"; import type {LayerSpecification, SourceSpecification} from "maplibre-gl"; import { InputButton } from "../InputButton"; import { Modal } from "./Modal"; import { FieldType } from "../FieldType"; import { FieldId } from "../FieldId"; import { FieldSource } from "../FieldSource"; import { FieldSourceLayer } from "../FieldSourceLayer"; import { NON_SOURCE_LAYERS } from "../../libs/non-source-layers"; type ModalAddInternalProps = { layers: LayerSpecification[] onLayersChange(layers: LayerSpecification[]): unknown isOpen: boolean onOpenToggle(): void // A dict of source id's and the available source layers sources: Record; } & WithTranslation; type ModalAddState = { type: LayerSpecification["type"] id: string source?: string "source-layer"?: string error?: string | null }; class ModalAddInternal extends React.Component { addLayer = () => { if (this.props.layers.some(l => l.id === this.state.id)) { this.setState({ error: this.props.t("Layer ID already exists") }); return; } const changedLayers = this.props.layers.slice(0); const layer: ModalAddState = { id: this.state.id, type: this.state.type, }; if(this.state.type !== "background") { layer.source = this.state.source; if(!NON_SOURCE_LAYERS.includes(this.state.type) && this.state["source-layer"]) { layer["source-layer"] = this.state["source-layer"]; } } changedLayers.push(layer as LayerSpecification); this.setState({ error: null }, () => { this.props.onLayersChange(changedLayers); this.props.onOpenToggle(); }); }; constructor(props: ModalAddInternalProps) { super(props); const state: ModalAddState = { type: "fill", id: "", error: null, }; if(Object.keys(props.sources).length > 0) { state.source = Object.keys(this.props.sources)[0]; const sourceLayers = this.props.sources[state.source].layers || []; if (sourceLayers.length > 0) { state["source-layer"] = sourceLayers[0]; } } this.state = state; } componentDidUpdate(_prevProps: ModalAddInternalProps, prevState: ModalAddState) { // Check if source is valid for new type const oldType = prevState.type; const newType = this.state.type; const availableSourcesOld = this.getSources(oldType); const availableSourcesNew = this.getSources(newType); if( // Type has changed oldType !== newType && prevState.source !== "" // Was a valid source previously && availableSourcesOld.indexOf(prevState.source!) > -1 // And is not a valid source now && availableSourcesNew.indexOf(this.state.source!) < 0 ) { // Clear the source this.setState({ source: "" }); } } getLayersForSource(source: string) { const sourceObj = this.props.sources[source] || {}; return sourceObj.layers || []; } getSources(type: LayerSpecification["type"]) { switch(type) { case "background": return []; case "hillshade": case "color-relief": return Object.entries(this.props.sources).filter(([_, v]) => v.type === "raster-dem").map(([k, _]) => k); case "raster": return Object.entries(this.props.sources).filter(([_, v]) => v.type === "raster").map(([k, _]) => k); case "heatmap": case "circle": case "fill": case "fill-extrusion": case "line": case "symbol": return Object.entries(this.props.sources).filter(([_, v]) => v.type === "vector" || v.type === "geojson").map(([k, _]) => k); default: return []; } } render() { const t = this.props.t; const sources = this.getSources(this.state.type); const layers = this.getLayersForSource(this.state.source!); let errorElement; if (this.state.error) { errorElement = (
{this.state.error} this.setState({ error: null })} className="maputnik-modal-error-close" > ×
); } return {errorElement}
{ this.setState({ id: v, error: null }); }} /> this.setState({ type: v })} /> {this.state.type !== "background" && this.setState({ source: v })} /> } {!NON_SOURCE_LAYERS.includes(this.state.type) && this.setState({ "source-layer": v })} /> } {t("Add Layer")}
; } } export const ModalAdd = withTranslation()(ModalAddInternal);