Migrate App to tsx

This commit is contained in:
HarelM
2023-12-24 10:00:03 +02:00
parent 90f50d98e7
commit 4f1ad9f24a
27 changed files with 250 additions and 133 deletions
+37
View File
@@ -72,6 +72,9 @@
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"@types/lodash.capitalize": "^4.2.9", "@types/lodash.capitalize": "^4.2.9",
"@types/lodash.clamp": "^4.0.9",
"@types/lodash.clonedeep": "^4.5.9",
"@types/lodash.get": "^4.4.9",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/lodash.throttle": "^4.1.9", "@types/lodash.throttle": "^4.1.9",
"@types/react": "^16.14.52", "@types/react": "^16.14.52",
@@ -83,6 +86,7 @@
"@types/react-dom": "^16.9.24", "@types/react-dom": "^16.9.24",
"@types/react-file-reader-input": "^2.0.4", "@types/react-file-reader-input": "^2.0.4",
"@types/react-icon-base": "^2.1.6", "@types/react-icon-base": "^2.1.6",
"@types/string-hash": "^1.1.3",
"@types/uuid": "^9.0.7", "@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.0", "@vitejs/plugin-react": "^4.2.0",
"cors": "^2.8.5", "cors": "^2.8.5",
@@ -4762,6 +4766,33 @@
"@types/lodash": "*" "@types/lodash": "*"
} }
}, },
"node_modules/@types/lodash.clamp": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/lodash.clamp/-/lodash.clamp-4.0.9.tgz",
"integrity": "sha512-t+hBIPHXyBVYkl0KEAEchOJwBrG8czt3E7r5fdpwMRrn3g+hkRzw6cjzWl+nJg3Z2QqRaQLt+W2n4ikwGr1u2g==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/lodash.clonedeep": {
"version": "4.5.9",
"resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz",
"integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/lodash.get": {
"version": "4.4.9",
"resolved": "https://registry.npmjs.org/@types/lodash.get/-/lodash.get-4.4.9.tgz",
"integrity": "sha512-J5dvW98sxmGnamqf+/aLP87PYXyrha9xIgc2ZlHl6OHMFR2Ejdxep50QfU0abO1+CH6+ugx+8wEUN1toImAinA==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/lodash.isequal": { "node_modules/@types/lodash.isequal": {
"version": "4.5.8", "version": "4.5.8",
"resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz", "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz",
@@ -5023,6 +5054,12 @@
"integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==",
"dev": true "dev": true
}, },
"node_modules/@types/string-hash": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@types/string-hash/-/string-hash-1.1.3.tgz",
"integrity": "sha512-p6skq756fJWiA59g2Uss+cMl6tpoDGuCBuxG0SI1t0NwJmYOU66LAMS6QiCgu7cUh3/hYCaMl5phcCW1JP5wOA==",
"dev": true
},
"node_modules/@types/tern": { "node_modules/@types/tern": {
"version": "0.23.9", "version": "0.23.9",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
+4
View File
@@ -101,6 +101,9 @@
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"@types/lodash.capitalize": "^4.2.9", "@types/lodash.capitalize": "^4.2.9",
"@types/lodash.clamp": "^4.0.9",
"@types/lodash.clonedeep": "^4.5.9",
"@types/lodash.get": "^4.4.9",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/lodash.throttle": "^4.1.9", "@types/lodash.throttle": "^4.1.9",
"@types/react": "^16.14.52", "@types/react": "^16.14.52",
@@ -112,6 +115,7 @@
"@types/react-dom": "^16.9.24", "@types/react-dom": "^16.9.24",
"@types/react-file-reader-input": "^2.0.4", "@types/react-file-reader-input": "^2.0.4",
"@types/react-icon-base": "^2.1.6", "@types/react-icon-base": "^2.1.6",
"@types/string-hash": "^1.1.3",
"@types/uuid": "^9.0.7", "@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.0", "@vitejs/plugin-react": "^4.2.0",
"cors": "^2.8.5", "cors": "^2.8.5",
+138 -75
View File
@@ -1,4 +1,5 @@
import autoBind from 'react-autobind'; // @ts-ignore
import autoBind from 'react-autobind'; // this can be easily replaced with arrow functions
import React from 'react' import React from 'react'
import cloneDeep from 'lodash.clonedeep' import cloneDeep from 'lodash.clonedeep'
import clamp from 'lodash.clamp' import clamp from 'lodash.clamp'
@@ -6,14 +7,15 @@ import buffer from 'buffer'
import get from 'lodash.get' import get from 'lodash.get'
import {unset} from 'lodash' import {unset} from 'lodash'
import {arrayMoveMutable} from 'array-move' import {arrayMoveMutable} from 'array-move'
import url from 'url'
import hash from "string-hash"; import hash from "string-hash";
import {Map, LayerSpecification, StyleSpecification, ValidationError, SourceSpecification} from 'maplibre-gl'
import {latest, validate} from '@maplibre/maplibre-gl-style-spec'
import MapMaplibreGl from './MapMaplibreGl' import MapMaplibreGl from './MapMaplibreGl'
import MapOpenLayers from './MapOpenLayers' import MapOpenLayers from './MapOpenLayers'
import LayerList from './LayerList' import LayerList from './LayerList'
import LayerEditor from './LayerEditor' import LayerEditor from './LayerEditor'
import AppToolbar from './AppToolbar' import AppToolbar, { MapState } from './AppToolbar'
import AppLayout from './AppLayout' import AppLayout from './AppLayout'
import MessagePanel from './AppMessagePanel' import MessagePanel from './AppMessagePanel'
@@ -25,8 +27,7 @@ import ModalShortcuts from './ModalShortcuts'
import ModalSurvey from './ModalSurvey' import ModalSurvey from './ModalSurvey'
import ModalDebug from './ModalDebug' import ModalDebug from './ModalDebug'
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata' import {downloadGlyphsMetadata, downloadSpriteMetadata} from '../libs/metadata'
import {latest, validate} from '@maplibre/maplibre-gl-style-spec'
import style from '../libs/style' import style from '../libs/style'
import { initialStyleUrl, loadStyleUrl, removeStyleQuerystring } from '../libs/urlopen' import { initialStyleUrl, loadStyleUrl, removeStyleQuerystring } from '../libs/urlopen'
import { undoMessages, redoMessages } from '../libs/diffmessage' import { undoMessages, redoMessages } from '../libs/diffmessage'
@@ -37,12 +38,13 @@ import LayerWatcher from '../libs/layerwatcher'
import tokens from '../config/tokens.json' import tokens from '../config/tokens.json'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import Debug from '../libs/debug' import Debug from '../libs/debug'
import {formatLayerId} from '../util/format'; import { SortEnd } from 'react-sortable-hoc';
import { MapOptions } from 'maplibre-gl';
// Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed. // Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed.
window.Buffer = buffer.Buffer; window.Buffer = buffer.Buffer;
function setFetchAccessToken(url, mapStyle) { function setFetchAccessToken(url: string, mapStyle: StyleSpecification) {
const matchesTilehosting = url.match(/\.tilehosting\.com/); const matchesTilehosting = url.match(/\.tilehosting\.com/);
const matchesMaptiler = url.match(/\.maptiler\.com/); const matchesMaptiler = url.match(/\.maptiler\.com/);
const matchesThunderforest = url.match(/\.thunderforest\.com/); const matchesThunderforest = url.match(/\.thunderforest\.com/);
@@ -63,7 +65,7 @@ function setFetchAccessToken(url, mapStyle) {
} }
} }
function updateRootSpec(spec, fieldName, newValues) { function updateRootSpec(spec: any, fieldName: string, newValues: any) {
return { return {
...spec, ...spec,
$root: { $root: {
@@ -76,15 +78,75 @@ function updateRootSpec(spec, fieldName, newValues) {
} }
} }
export default class App extends React.Component { type OnStyleChangedOpts = {
constructor(props) { save?: boolean
addRevision?: boolean
initialLoad?: boolean
}
type MappedErrors = {
message: string
parsed?: {
type: string
data: {
index: number
key: string
message: string
}
}
}
type AppState = {
errors: MappedErrors[],
infos: string[],
mapStyle: StyleSpecification & {id: string},
dirtyMapStyle?: StyleSpecification,
selectedLayerIndex: number,
selectedLayerOriginalId?: string,
sources: {[key: string]: SourceSpecification},
vectorLayers: {},
spec: any,
mapView: {
zoom: number,
center: {
lng: number,
lat: number,
},
},
maplibreGlDebugOptions: Partial<MapOptions> & {
showTileBoundaries: boolean,
showCollisionBoxes: boolean,
showOverdrawInspector: boolean,
},
openlayersDebugOptions: {
debugToolbox: boolean,
},
mapState: MapState
isOpen: {
settings: boolean
sources: boolean
open: boolean
shortcuts: boolean
export: boolean
survey: boolean
debug: boolean
}
}
export default class App extends React.Component<any, AppState> {
revisionStore: RevisionStore;
styleStore: StyleStore | ApiStyleStore;
layerWatcher: LayerWatcher;
shortcutEl: ModalShortcuts | null = null;
constructor(props: any) {
super(props) super(props)
autoBind(this); autoBind(this);
this.revisionStore = new RevisionStore() this.revisionStore = new RevisionStore()
const params = new URLSearchParams(window.location.search.substring(1)) const params = new URLSearchParams(window.location.search.substring(1))
let port = params.get("localport") let port = params.get("localport")
if (port == null && (window.location.port != 80 && window.location.port != 443)) { if (port == null && (window.location.port !== "80" && window.location.port !== "443")) {
port = window.location.port port = window.location.port
} }
this.styleStore = new ApiStyleStore({ this.styleStore = new ApiStyleStore({
@@ -136,7 +198,7 @@ export default class App extends React.Component {
{ {
key: "m", key: "m",
handler: () => { handler: () => {
document.querySelector(".maplibregl-canvas").focus(); (document.querySelector(".maplibregl-canvas") as HTMLCanvasElement).focus();
} }
}, },
{ {
@@ -149,7 +211,7 @@ export default class App extends React.Component {
document.body.addEventListener("keyup", (e) => { document.body.addEventListener("keyup", (e) => {
if(e.key === "Escape") { if(e.key === "Escape") {
e.target.blur(); (e.target as HTMLElement).blur();
document.body.focus(); document.body.focus();
} }
else if(this.state.isOpen.shortcuts || document.activeElement === document.body) { else if(this.state.isOpen.shortcuts || document.activeElement === document.body) {
@@ -159,7 +221,7 @@ export default class App extends React.Component {
if(shortcut) { if(shortcut) {
this.setModal("shortcuts", false); this.setModal("shortcuts", false);
shortcut.handler(e); shortcut.handler();
} }
} }
}) })
@@ -192,8 +254,6 @@ export default class App extends React.Component {
Debug.set("maputnik", "styleStore", this.styleStore); Debug.set("maputnik", "styleStore", this.styleStore);
} }
const queryObj = url.parse(window.location.href, true).query;
this.state = { this.state = {
errors: [], errors: [],
infos: [], infos: [],
@@ -235,25 +295,25 @@ export default class App extends React.Component {
}) })
} }
handleKeyPress = (e) => { handleKeyPress = (e: KeyboardEvent) => {
if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) { if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
if(e.metaKey && e.shiftKey && e.keyCode === 90) { if(e.metaKey && e.shiftKey && e.keyCode === 90) {
e.preventDefault(); e.preventDefault();
this.onRedo(e); this.onRedo();
} }
else if(e.metaKey && e.keyCode === 90) { else if(e.metaKey && e.keyCode === 90) {
e.preventDefault(); e.preventDefault();
this.onUndo(e); this.onUndo();
} }
} }
else { else {
if(e.ctrlKey && e.keyCode === 90) { if(e.ctrlKey && e.keyCode === 90) {
e.preventDefault(); e.preventDefault();
this.onUndo(e); this.onUndo();
} }
else if(e.ctrlKey && e.keyCode === 89) { else if(e.ctrlKey && e.keyCode === 89) {
e.preventDefault(); e.preventDefault();
this.onRedo(e); this.onRedo();
} }
} }
} }
@@ -266,12 +326,12 @@ export default class App extends React.Component {
window.removeEventListener("keydown", this.handleKeyPress); window.removeEventListener("keydown", this.handleKeyPress);
} }
saveStyle(snapshotStyle) { saveStyle(snapshotStyle: StyleSpecification & {id: string}) {
this.styleStore.save(snapshotStyle) this.styleStore.save(snapshotStyle)
} }
updateFonts(urlTemplate) { updateFonts(urlTemplate: string) {
const metadata = this.state.mapStyle.metadata || {} const metadata: {[key: string]: string} = this.state.mapStyle.metadata || {} as any
const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles
let glyphUrl = (typeof urlTemplate === 'string')? urlTemplate.replace('{key}', accessToken): urlTemplate; let glyphUrl = (typeof urlTemplate === 'string')? urlTemplate.replace('{key}', accessToken): urlTemplate;
@@ -280,13 +340,13 @@ export default class App extends React.Component {
}) })
} }
updateIcons(baseUrl) { updateIcons(baseUrl: string) {
downloadSpriteMetadata(baseUrl, icons => { downloadSpriteMetadata(baseUrl, icons => {
this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)}) this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)})
}) })
} }
onChangeMetadataProperty = (property, value) => { onChangeMetadataProperty = (property: string, value: any) => {
// If we're changing renderer reset the map state. // If we're changing renderer reset the map state.
if ( if (
property === 'maputnik:renderer' && property === 'maputnik:renderer' &&
@@ -300,14 +360,14 @@ export default class App extends React.Component {
const changedStyle = { const changedStyle = {
...this.state.mapStyle, ...this.state.mapStyle,
metadata: { metadata: {
...this.state.mapStyle.metadata, ...(this.state.mapStyle as any).metadata,
[property]: value [property]: value
} }
} }
this.onStyleChanged(changedStyle) this.onStyleChanged(changedStyle)
} }
onStyleChanged = (newStyle, opts={}) => { onStyleChanged = (newStyle: StyleSpecification & {id: string}, opts: OnStyleChangedOpts={}) => {
opts = { opts = {
save: true, save: true,
addRevision: true, addRevision: true,
@@ -319,16 +379,16 @@ export default class App extends React.Component {
this.getInitialStateFromUrl(newStyle); this.getInitialStateFromUrl(newStyle);
} }
const errors = validate(newStyle, latest) || []; // This "any" can be removed in latest version of maplibre where maplibre re-exported types from style-spec
const errors = validate(newStyle as any, latest) || [];
// The validate function doesn't give us errors for duplicate error with // The validate function doesn't give us errors for duplicate error with
// empty string for layer.id, manually deal with that here. // empty string for layer.id, manually deal with that here.
const layerErrors = []; const layerErrors: (Error | ValidationError)[] = [];
if (newStyle && newStyle.layers) { if (newStyle && newStyle.layers) {
const foundLayers = new Map(); const foundLayers = new global.Map();
newStyle.layers.forEach((layer, index) => { newStyle.layers.forEach((layer, index) => {
if (layer.id === "" && foundLayers.has(layer.id)) { if (layer.id === "" && foundLayers.has(layer.id)) {
const message = `Duplicate layer: ${formatLayerId(layer.id)}`;
const error = new Error( const error = new Error(
`layers[${index}]: duplicate layer id [empty_string], previously used` `layers[${index}]: duplicate layer id [empty_string], previously used`
); );
@@ -342,7 +402,7 @@ export default class App extends React.Component {
// Special case: Duplicate layer id // Special case: Duplicate layer id
const dupMatch = error.message.match(/layers\[(\d+)\]: (duplicate layer id "?(.*)"?, previously used)/); const dupMatch = error.message.match(/layers\[(\d+)\]: (duplicate layer id "?(.*)"?, previously used)/);
if (dupMatch) { if (dupMatch) {
const [matchStr, index, message] = dupMatch; const [_matchStr, index, message] = dupMatch;
return { return {
message: error.message, message: error.message,
parsed: { parsed: {
@@ -359,7 +419,7 @@ export default class App extends React.Component {
// Special case: Invalid source // Special case: Invalid source
const invalidSourceMatch = error.message.match(/layers\[(\d+)\]: (source "(?:.*)" not found)/); const invalidSourceMatch = error.message.match(/layers\[(\d+)\]: (source "(?:.*)" not found)/);
if (invalidSourceMatch) { if (invalidSourceMatch) {
const [matchStr, index, message] = invalidSourceMatch; const [_matchStr, index, message] = invalidSourceMatch;
return { return {
message: error.message, message: error.message,
parsed: { parsed: {
@@ -375,7 +435,7 @@ export default class App extends React.Component {
const layerMatch = error.message.match(/layers\[(\d+)\]\.(?:(\S+)\.)?(\S+): (.*)/); const layerMatch = error.message.match(/layers\[(\d+)\]\.(?:(\S+)\.)?(\S+): (.*)/);
if (layerMatch) { if (layerMatch) {
const [matchStr, index, group, property, message] = layerMatch; const [_matchStr, index, group, property, message] = layerMatch;
const key = (group && property) ? [group, property].join(".") : property; const key = (group && property) ? [group, property].join(".") : property;
return { return {
message: error.message, message: error.message,
@@ -396,7 +456,7 @@ export default class App extends React.Component {
} }
}); });
let dirtyMapStyle = undefined; let dirtyMapStyle: StyleSpecification | undefined = undefined;
if (errors.length > 0) { if (errors.length > 0) {
dirtyMapStyle = cloneDeep(newStyle); dirtyMapStyle = cloneDeep(newStyle);
@@ -406,7 +466,7 @@ export default class App extends React.Component {
try { try {
const objPath = message.split(":")[0]; const objPath = message.split(":")[0];
// Errors can be deply nested for example 'layers[0].filter[1][1][0]' we only care upto the property 'layers[0].filter' // Errors can be deply nested for example 'layers[0].filter[1][1][0]' we only care upto the property 'layers[0].filter'
const unsetPath = objPath.match(/^\S+?\[\d+\]\.[^\[]+/)[0]; const unsetPath = objPath.match(/^\S+?\[\d+\]\.[^\[]+/)![0];
unset(dirtyMapStyle, unsetPath); unset(dirtyMapStyle, unsetPath);
} }
catch (err) { catch (err) {
@@ -417,17 +477,17 @@ export default class App extends React.Component {
} }
if(newStyle.glyphs !== this.state.mapStyle.glyphs) { if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
this.updateFonts(newStyle.glyphs) this.updateFonts(newStyle.glyphs as string)
} }
if(newStyle.sprite !== this.state.mapStyle.sprite) { if(newStyle.sprite !== this.state.mapStyle.sprite) {
this.updateIcons(newStyle.sprite) this.updateIcons(newStyle.sprite as string)
} }
if (opts.addRevision) { if (opts.addRevision) {
this.revisionStore.addRevision(newStyle); this.revisionStore.addRevision(newStyle);
} }
if (opts.save) { if (opts.save) {
this.saveStyle(newStyle); this.saveStyle(newStyle as StyleSpecification & {id: string});
} }
this.setState({ this.setState({
@@ -460,7 +520,7 @@ export default class App extends React.Component {
}) })
} }
onMoveLayer = (move) => { onMoveLayer = (move: SortEnd) => {
let { oldIndex, newIndex } = move; let { oldIndex, newIndex } = move;
let layers = this.state.mapStyle.layers; let layers = this.state.mapStyle.layers;
oldIndex = clamp(oldIndex, 0, layers.length-1); oldIndex = clamp(oldIndex, 0, layers.length-1);
@@ -478,7 +538,7 @@ export default class App extends React.Component {
this.onLayersChange(layers); this.onLayersChange(layers);
} }
onLayersChange = (changedLayers) => { onLayersChange = (changedLayers: LayerSpecification[]) => {
const changedStyle = { const changedStyle = {
...this.state.mapStyle, ...this.state.mapStyle,
layers: changedLayers layers: changedLayers
@@ -486,14 +546,14 @@ export default class App extends React.Component {
this.onStyleChanged(changedStyle) this.onStyleChanged(changedStyle)
} }
onLayerDestroy = (index) => { onLayerDestroy = (index: number) => {
let layers = this.state.mapStyle.layers; let layers = this.state.mapStyle.layers;
const remainingLayers = layers.slice(0); const remainingLayers = layers.slice(0);
remainingLayers.splice(index, 1); remainingLayers.splice(index, 1);
this.onLayersChange(remainingLayers); this.onLayersChange(remainingLayers);
} }
onLayerCopy = (index) => { onLayerCopy = (index: number) => {
let layers = this.state.mapStyle.layers; let layers = this.state.mapStyle.layers;
const changedLayers = layers.slice(0) const changedLayers = layers.slice(0)
@@ -503,7 +563,7 @@ export default class App extends React.Component {
this.onLayersChange(changedLayers) this.onLayersChange(changedLayers)
} }
onLayerVisibilityToggle = (index) => { onLayerVisibilityToggle = (index: number) => {
let layers = this.state.mapStyle.layers; let layers = this.state.mapStyle.layers;
const changedLayers = layers.slice(0) const changedLayers = layers.slice(0)
@@ -517,7 +577,7 @@ export default class App extends React.Component {
} }
onLayerIdChange = (index, oldId, newId) => { onLayerIdChange = (index: number, _oldId: string, newId: string) => {
const changedLayers = this.state.mapStyle.layers.slice(0) const changedLayers = this.state.mapStyle.layers.slice(0)
changedLayers[index] = { changedLayers[index] = {
...changedLayers[index], ...changedLayers[index],
@@ -527,26 +587,26 @@ export default class App extends React.Component {
this.onLayersChange(changedLayers) this.onLayersChange(changedLayers)
} }
onLayerChanged = (index, layer) => { onLayerChanged = (index: number, layer: LayerSpecification) => {
const changedLayers = this.state.mapStyle.layers.slice(0) const changedLayers = this.state.mapStyle.layers.slice(0)
changedLayers[index] = layer changedLayers[index] = layer
this.onLayersChange(changedLayers) this.onLayersChange(changedLayers)
} }
setMapState = (newState) => { setMapState = (newState: MapState) => {
this.setState({ this.setState({
mapState: newState mapState: newState
}, this.setStateInUrl); }, this.setStateInUrl);
} }
setDefaultValues = (styleObj) => { setDefaultValues = (styleObj: StyleSpecification & {id: string}) => {
const metadata = styleObj.metadata || {} const metadata: {[key: string]: string} = styleObj.metadata || {} as any
if(metadata['maputnik:renderer'] === undefined) { if(metadata['maputnik:renderer'] === undefined) {
const changedStyle = { const changedStyle = {
...styleObj, ...styleObj,
metadata: { metadata: {
...styleObj.metadata, ...styleObj.metadata as any,
'maputnik:renderer': 'mlgljs' 'maputnik:renderer': 'mlgljs'
} }
} }
@@ -556,13 +616,13 @@ export default class App extends React.Component {
} }
} }
openStyle = (styleObj) => { openStyle = (styleObj: StyleSpecification & {id: string}) => {
styleObj = this.setDefaultValues(styleObj) styleObj = this.setDefaultValues(styleObj)
this.onStyleChanged(styleObj) this.onStyleChanged(styleObj)
} }
fetchSources() { fetchSources() {
const sourceList = {}; const sourceList: {[key: string]: any} = {};
for(let [key, val] of Object.entries(this.state.mapStyle.sources)) { for(let [key, val] of Object.entries(this.state.mapStyle.sources)) {
if( if(
@@ -578,12 +638,12 @@ export default class App extends React.Component {
let url = val.url; let url = val.url;
try { try {
url = setFetchAccessToken(url, this.state.mapStyle) url = setFetchAccessToken(url!, this.state.mapStyle)
} catch(err) { } catch(err) {
console.warn("Failed to setFetchAccessToken: ", err); console.warn("Failed to setFetchAccessToken: ", err);
} }
fetch(url, { fetch(url!, {
mode: 'cors', mode: 'cors',
}) })
.then(response => response.json()) .then(response => response.json())
@@ -599,7 +659,7 @@ export default class App extends React.Component {
}); });
for(let layer of json.vector_layers) { for(let layer of json.vector_layers) {
sources[key].layers.push(layer.id) (sources[key] as any).layers.push(layer.id)
} }
console.debug("Updating source: "+key); console.debug("Updating source: "+key);
@@ -625,11 +685,17 @@ export default class App extends React.Component {
} }
_getRenderer () { _getRenderer () {
const metadata = this.state.mapStyle.metadata || {}; const metadata: {[key:string]: string} = this.state.mapStyle.metadata || {} as any;
return metadata['maputnik:renderer'] || 'mlgljs'; return metadata['maputnik:renderer'] || 'mlgljs';
} }
onMapChange = (mapView) => { onMapChange = (mapView: {
zoom: number,
center: {
lng: number,
lat: number,
},
}) => {
this.setState({ this.setState({
mapView, mapView,
}); });
@@ -637,16 +703,15 @@ export default class App extends React.Component {
mapRenderer() { mapRenderer() {
const {mapStyle, dirtyMapStyle} = this.state; const {mapStyle, dirtyMapStyle} = this.state;
const metadata = this.state.mapStyle.metadata || {};
const mapProps = { const mapProps = {
mapStyle: (dirtyMapStyle || mapStyle), mapStyle: (dirtyMapStyle || mapStyle),
replaceAccessTokens: (mapStyle) => { replaceAccessTokens: (mapStyle: StyleSpecification) => {
return style.replaceAccessTokens(mapStyle, { return style.replaceAccessTokens(mapStyle, {
allowFallback: true allowFallback: true
}); });
}, },
onDataChange: (e) => { onDataChange: (e: {map: Map}) => {
this.layerWatcher.analyzeMap(e.map) this.layerWatcher.analyzeMap(e.map)
this.fetchSources(); this.fetchSources();
}, },
@@ -677,7 +742,7 @@ export default class App extends React.Component {
if(this.state.mapState.match(/^filter-/)) { if(this.state.mapState.match(/^filter-/)) {
filterName = this.state.mapState.replace(/^filter-/, ""); filterName = this.state.mapState.replace(/^filter-/, "");
} }
const elementStyle = {}; const elementStyle: {filter?: string} = {};
if (filterName) { if (filterName) {
elementStyle.filter = `url('#${filterName}')`; elementStyle.filter = `url('#${filterName}')`;
} }
@@ -715,12 +780,12 @@ export default class App extends React.Component {
history.replaceState({selectedLayerIndex}, "Maputnik", url.href); history.replaceState({selectedLayerIndex}, "Maputnik", url.href);
} }
getInitialStateFromUrl = (mapStyle) => { getInitialStateFromUrl = (mapStyle: StyleSpecification) => {
const url = new URL(location.href); const url = new URL(location.href);
const modalParam = url.searchParams.get("modal"); const modalParam = url.searchParams.get("modal");
if (modalParam && modalParam !== "") { if (modalParam && modalParam !== "") {
const modals = modalParam.split(","); const modals = modalParam.split(",");
const modalObj = {}; const modalObj: {[key: string]: boolean} = {};
modals.forEach(modalName => { modals.forEach(modalName => {
modalObj[modalName] = true; modalObj[modalName] = true;
}); });
@@ -735,7 +800,7 @@ export default class App extends React.Component {
const view = url.searchParams.get("view"); const view = url.searchParams.get("view");
if (view && view !== "") { if (view && view !== "") {
this.setMapState(view); this.setMapState(view as MapState);
} }
const path = url.searchParams.get("layer"); const path = url.searchParams.get("layer");
@@ -767,14 +832,14 @@ export default class App extends React.Component {
} }
} }
onLayerSelect = (index) => { onLayerSelect = (index: number) => {
this.setState({ this.setState({
selectedLayerIndex: index, selectedLayerIndex: index,
selectedLayerOriginalId: this.state.mapStyle.layers[index].id, selectedLayerOriginalId: this.state.mapStyle.layers[index].id,
}, this.setStateInUrl); }, this.setStateInUrl);
} }
setModal(modalName, value) { setModal(modalName: keyof AppState["isOpen"], value: boolean) {
if(modalName === 'survey' && value === false) { if(modalName === 'survey' && value === false) {
localStorage.setItem('survey', ''); localStorage.setItem('survey', '');
} }
@@ -787,11 +852,11 @@ export default class App extends React.Component {
}, this.setStateInUrl) }, this.setStateInUrl)
} }
toggleModal(modalName) { toggleModal(modalName: keyof AppState["isOpen"]) {
this.setModal(modalName, !this.state.isOpen[modalName]); this.setModal(modalName, !this.state.isOpen[modalName]);
} }
onChangeOpenlayersDebug = (key, value) => { onChangeOpenlayersDebug = (key: keyof AppState["openlayersDebugOptions"], value: boolean) => {
this.setState({ this.setState({
openlayersDebugOptions: { openlayersDebugOptions: {
...this.state.openlayersDebugOptions, ...this.state.openlayersDebugOptions,
@@ -800,7 +865,7 @@ export default class App extends React.Component {
}); });
} }
onChangeMaplibreGlDebug = (key, value) => { onChangeMaplibreGlDebug = (key: keyof AppState["maplibreGlDebugOptions"], value: any) => {
this.setState({ this.setState({
maplibreGlDebugOptions: { maplibreGlDebugOptions: {
...this.state.maplibreGlDebugOptions, ...this.state.maplibreGlDebugOptions,
@@ -811,8 +876,7 @@ export default class App extends React.Component {
render() { render() {
const layers = this.state.mapStyle.layers || [] const layers = this.state.mapStyle.layers || []
const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : null const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : undefined
const metadata = this.state.mapStyle.metadata || {}
const toolbar = <AppToolbar const toolbar = <AppToolbar
renderer={this._getRenderer()} renderer={this._getRenderer()}
@@ -855,7 +919,7 @@ export default class App extends React.Component {
onLayerVisibilityToggle={this.onLayerVisibilityToggle} onLayerVisibilityToggle={this.onLayerVisibilityToggle}
onLayerIdChange={this.onLayerIdChange} onLayerIdChange={this.onLayerIdChange}
errors={this.state.errors} errors={this.state.errors}
/> : null /> : undefined
const bottomPanel = (this.state.errors.length + this.state.infos.length) > 0 ? <MessagePanel const bottomPanel = (this.state.errors.length + this.state.infos.length) > 0 ? <MessagePanel
currentLayer={selectedLayer} currentLayer={selectedLayer}
@@ -864,7 +928,7 @@ export default class App extends React.Component {
mapStyle={this.state.mapStyle} mapStyle={this.state.mapStyle}
errors={this.state.errors} errors={this.state.errors}
infos={this.state.infos} infos={this.state.infos}
/> : null /> : undefined
const modals = <div> const modals = <div>
@@ -889,7 +953,6 @@ export default class App extends React.Component {
onChangeMetadataProperty={this.onChangeMetadataProperty} onChangeMetadataProperty={this.onChangeMetadataProperty}
isOpen={this.state.isOpen.settings} isOpen={this.state.isOpen.settings}
onOpenToggle={this.toggleModal.bind(this, 'settings')} onOpenToggle={this.toggleModal.bind(this, 'settings')}
openlayersDebugOptions={this.state.openlayersDebugOptions}
/> />
<ModalExport <ModalExport
mapStyle={this.state.mapStyle} mapStyle={this.state.mapStyle}
+2 -2
View File
@@ -1,13 +1,13 @@
import React from 'react' import React from 'react'
import {formatLayerId} from '../util/format'; import {formatLayerId} from '../util/format';
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec'; import {LayerSpecification, StyleSpecification} from 'maplibre-gl';
type AppMessagePanelProps = { type AppMessagePanelProps = {
errors?: unknown[] errors?: unknown[]
infos?: unknown[] infos?: unknown[]
mapStyle?: StyleSpecification mapStyle?: StyleSpecification
onLayerSelect?(...args: unknown[]): unknown onLayerSelect?(...args: unknown[]): unknown
currentLayer?: object currentLayer?: LayerSpecification
selectedLayerIndex?: number selectedLayerIndex?: number
}; };
+6 -4
View File
@@ -98,6 +98,8 @@ class ToolbarAction extends React.Component<ToolbarActionProps> {
} }
} }
export type MapState = "map" | "inspect" | "filter-achromatopsia" | "filter-deuteranopia" | "filter-protanopia" | "filter-tritanopia";
type AppToolbarProps = { type AppToolbarProps = {
mapStyle: object mapStyle: object
inspectModeEnabled: boolean inspectModeEnabled: boolean
@@ -108,8 +110,8 @@ type AppToolbarProps = {
sources: object sources: object
children?: React.ReactNode children?: React.ReactNode
onToggleModal(...args: unknown[]): unknown onToggleModal(...args: unknown[]): unknown
onSetMapState(...args: unknown[]): unknown onSetMapState(mapState: MapState): unknown
mapState?: string mapState?: MapState
renderer?: string renderer?: string
}; };
@@ -124,7 +126,7 @@ export default class AppToolbar extends React.Component<AppToolbarProps> {
} }
} }
handleSelection(val: string | undefined) { handleSelection(val: MapState) {
this.props.onSetMapState(val); this.props.onSetMapState(val);
} }
@@ -245,7 +247,7 @@ export default class AppToolbar extends React.Component<AppToolbarProps> {
<select <select
className="maputnik-select" className="maputnik-select"
data-wd-key="maputnik-select" data-wd-key="maputnik-select"
onChange={(e) => this.handleSelection(e.target.value)} onChange={(e) => this.handleSelection(e.target.value as MapState)}
value={currentView?.id} value={currentView?.id}
> >
{views.filter(v => v.group === "general").map((item) => { {views.filter(v => v.group === "general").map((item) => {
+6 -4
View File
@@ -1,9 +1,11 @@
import React from 'react' import React from 'react'
import {combiningFilterOps} from '../libs/filterops'
import {mdiTableRowPlusAfter} from '@mdi/js'; import {mdiTableRowPlusAfter} from '@mdi/js';
import {isEqual} from 'lodash'; import {isEqual} from 'lodash';
import {ExpressionSpecification, LegacyFilterSpecification, StyleSpecification} from 'maplibre-gl'
import {latest, migrate, convertFilter} from '@maplibre/maplibre-gl-style-spec'
import {mdiFunctionVariant} from '@mdi/js';
import {latest, migrate, convertFilter, ExpressionSpecification, LegacyFilterSpecification, StyleSpecification} from '@maplibre/maplibre-gl-style-spec' import {combiningFilterOps} from '../libs/filterops'
import InputSelect from './InputSelect' import InputSelect from './InputSelect'
import Block from './Block' import Block from './Block'
import SingleFilterEditor from './SingleFilterEditor' import SingleFilterEditor from './SingleFilterEditor'
@@ -11,7 +13,6 @@ import FilterEditorBlock from './FilterEditorBlock'
import InputButton from './InputButton' import InputButton from './InputButton'
import Doc from './Doc' import Doc from './Doc'
import ExpressionProperty from './_ExpressionProperty'; import ExpressionProperty from './_ExpressionProperty';
import {mdiFunctionVariant} from '@mdi/js';
function combiningFilter(props: FilterEditorProps): LegacyFilterSpecification | ExpressionSpecification { function combiningFilter(props: FilterEditorProps): LegacyFilterSpecification | ExpressionSpecification {
@@ -33,7 +34,8 @@ function combiningFilter(props: FilterEditorProps): LegacyFilterSpecification |
} }
function migrateFilter(filter: LegacyFilterSpecification | ExpressionSpecification) { function migrateFilter(filter: LegacyFilterSpecification | ExpressionSpecification) {
return (migrate(createStyleFromFilter(filter)).layers[0] as any).filter; // This "any" can be removed in latest version of maplibre where maplibre re-exported types from style-spec
return (migrate(createStyleFromFilter(filter) as any).layers[0] as any).filter;
} }
function createStyleFromFilter(filter: LegacyFilterSpecification | ExpressionSpecification): StyleSpecification & {id: string} { function createStyleFromFilter(filter: LegacyFilterSpecification | ExpressionSpecification): StyleSpecification & {id: string} {
-1
View File
@@ -137,7 +137,6 @@ class DeleteValueInputButton extends React.Component<DeleteValueInputButtonProps
> >
<FieldDocLabel <FieldDocLabel
label={<MdDelete />} label={<MdDelete />}
fieldSpec={{doc:" Remove array item."}}
/> />
</InputButton> </InputButton>
} }
+5 -5
View File
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton' import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton'
import {Accordion} from 'react-accessible-accordion'; import {Accordion} from 'react-accessible-accordion';
import {MdMoreVert} from 'react-icons/md' import {MdMoreVert} from 'react-icons/md'
import {BackgroundLayerSpecification, LayerSpecification, SourceSpecification} from 'maplibre-gl';
import FieldJson from './FieldJson' import FieldJson from './FieldJson'
import FilterEditor from './FilterEditor' import FilterEditor from './FilterEditor'
@@ -18,8 +19,6 @@ import FieldSourceLayer from './FieldSourceLayer'
import { changeType, changeProperty } from '../libs/layer' import { changeType, changeProperty } from '../libs/layer'
import layout from '../config/layout.json' import layout from '../config/layout.json'
import {formatLayerId} from '../util/format'; import {formatLayerId} from '../util/format';
import { SourceSpecification } from '@maplibre/maplibre-gl-style-spec';
import { BackgroundLayerSpecification, LayerSpecification } from 'maplibre-gl';
function getLayoutForType(type: LayerSpecification["type"]) { function getLayoutForType(type: LayerSpecification["type"]) {
@@ -46,7 +45,7 @@ function layoutGroups(layerType: LayerSpecification["type"]): {title: string, ty
type LayerEditorProps = { type LayerEditorProps = {
layer: LayerSpecification layer: LayerSpecification
sources?: SourceSpecification sources: {[key: string]: SourceSpecification}
vectorLayers: {[key: string]: any} vectorLayers: {[key: string]: any}
spec: object spec: object
onLayerChanged(...args: unknown[]): unknown onLayerChanged(...args: unknown[]): unknown
@@ -150,8 +149,9 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
}) })
let sourceLayerIds; let sourceLayerIds;
if(this.props.sources?.hasOwnProperty((this.props.layer as Exclude<LayerSpecification, BackgroundLayerSpecification>).source)) { const layer = this.props.layer as Exclude<LayerSpecification, BackgroundLayerSpecification>;
sourceLayerIds = (this.props.sources as any)[(this.props.layer as any).source].layers; if(this.props.sources.hasOwnProperty(layer.source)) {
sourceLayerIds = (this.props.sources[layer.source] as any).layers;
} }
switch(type) { switch(type) {
+4 -4
View File
@@ -6,8 +6,8 @@ import LayerListGroup from './LayerListGroup'
import LayerListItem from './LayerListItem' import LayerListItem from './LayerListItem'
import ModalAdd from './ModalAdd' import ModalAdd from './ModalAdd'
import {SortableContainer} from 'react-sortable-hoc'; import {SortEndHandler, SortableContainer} from 'react-sortable-hoc';
import { LayerSpecification } from '@maplibre/maplibre-gl-style-spec'; import type {LayerSpecification} from 'maplibre-gl';
function layerPrefix(name: string) { function layerPrefix(name: string) {
return name.replace(' ', '-').replace('_', '-').split('-')[0] return name.replace(' ', '-').replace('_', '-').split('-')[0]
@@ -32,7 +32,7 @@ let UID = 0;
type LayerListContainerProps = { type LayerListContainerProps = {
layers: LayerSpecification[] layers: LayerSpecification[]
selectedLayerIndex: number selectedLayerIndex: number
onLayersChange(...args: unknown[]): unknown onLayersChange(layers: LayerSpecification[]): unknown
onLayerSelect(...args: unknown[]): unknown onLayerSelect(...args: unknown[]): unknown
onLayerDestroy?(...args: unknown[]): unknown onLayerDestroy?(...args: unknown[]): unknown
onLayerCopy(...args: unknown[]): unknown onLayerCopy(...args: unknown[]): unknown
@@ -331,7 +331,7 @@ class LayerListContainer extends React.Component<LayerListContainerProps, LayerL
const LayerListContainerSortable = SortableContainer((props: LayerListContainerProps) => <LayerListContainer {...props} />) const LayerListContainerSortable = SortableContainer((props: LayerListContainerProps) => <LayerListContainer {...props} />)
type LayerListProps = LayerListContainerProps & { type LayerListProps = LayerListContainerProps & {
onMoveLayer(...args: unknown[]): unknown onMoveLayer: SortEndHandler
}; };
export default class LayerList extends React.Component<LayerListProps> { export default class LayerList extends React.Component<LayerListProps> {
+4 -4
View File
@@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import MapLibreGl, {LayerSpecification, Map, MapOptions, SourceSpecification, StyleSpecification} from 'maplibre-gl' import MapLibreGl, {LayerSpecification, LngLat, Map, MapOptions, SourceSpecification, StyleSpecification} from 'maplibre-gl'
// @ts-ignore // @ts-ignore
import MapboxInspect from 'mapbox-gl-inspect' import MapboxInspect from 'mapbox-gl-inspect'
// @ts-ignore // @ts-ignore
@@ -53,18 +53,18 @@ function buildInspectStyle(originalMapStyle: StyleSpecification, coloredLayers:
} }
type MapMaplibreGlProps = { type MapMaplibreGlProps = {
onDataChange?(...args: unknown[]): unknown onDataChange?(event: {map: Map | null}): unknown
onLayerSelect(...args: unknown[]): unknown onLayerSelect(...args: unknown[]): unknown
mapStyle: StyleSpecification mapStyle: StyleSpecification
inspectModeEnabled: boolean inspectModeEnabled: boolean
highlightedLayer?: HighlightedLayer highlightedLayer?: HighlightedLayer
options?: MapOptions & { options?: Partial<MapOptions> & {
showTileBoundaries?: boolean showTileBoundaries?: boolean
showCollisionBoxes?: boolean showCollisionBoxes?: boolean
showOverdrawInspector?: boolean showOverdrawInspector?: boolean
} }
replaceAccessTokens(mapStyle: StyleSpecification): StyleSpecification replaceAccessTokens(mapStyle: StyleSpecification): StyleSpecification
onChange(...args: unknown[]): unknown onChange(value: {center: LngLat, zoom: number}): unknown
}; };
type MapMaplibreGlState = { type MapMaplibreGlState = {
+1 -1
View File
@@ -8,7 +8,7 @@ import {apply} from 'ol-mapbox-style';
import {Map, View, Overlay} from 'ol'; import {Map, View, Overlay} from 'ol';
import {toLonLat} from 'ol/proj'; import {toLonLat} from 'ol/proj';
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec'; import type {StyleSpecification} from 'maplibre-gl';
function renderCoords (coords: string[]) { function renderCoords (coords: string[]) {
+1 -1
View File
@@ -8,7 +8,7 @@ type ModalProps = {
"data-wd-key"?: string "data-wd-key"?: string
isOpen: boolean isOpen: boolean
title: string title: string
onOpenToggle(...args: unknown[]): unknown onOpenToggle(value: boolean): unknown
underlayClickExits?: boolean underlayClickExits?: boolean
underlayProps?: any underlayProps?: any
className?: string className?: string
+7 -6
View File
@@ -6,18 +6,19 @@ import FieldType from './FieldType'
import FieldId from './FieldId' import FieldId from './FieldId'
import FieldSource from './FieldSource' import FieldSource from './FieldSource'
import FieldSourceLayer from './FieldSourceLayer' import FieldSourceLayer from './FieldSourceLayer'
import type {LayerSpecification} from 'maplibre-gl'
type ModalAddProps = { type ModalAddProps = {
layers: unknown[] layers: LayerSpecification[]
onLayersChange(...args: unknown[]): unknown onLayersChange(layers: LayerSpecification[]): unknown
isOpen: boolean isOpen: boolean
onOpenToggle(...args: unknown[]): unknown onOpenToggle(open: boolean): unknown
// A dict of source id's and the available source layers // A dict of source id's and the available source layers
sources: any sources: any
}; };
type ModalAddState = { type ModalAddState = {
type: string type: LayerSpecification["type"]
id: string id: string
source?: string source?: string
'source-layer'?: string 'source-layer'?: string
@@ -38,7 +39,7 @@ export default class ModalAdd extends React.Component<ModalAddProps, ModalAddSta
} }
} }
changedLayers.push(layer) changedLayers.push(layer as LayerSpecification)
this.props.onLayersChange(changedLayers) this.props.onLayersChange(changedLayers)
this.props.onOpenToggle(false) this.props.onOpenToggle(false)
@@ -145,7 +146,7 @@ export default class ModalAdd extends React.Component<ModalAddProps, ModalAddSta
<FieldType <FieldType
value={this.state.type} value={this.state.type}
wdKey="add-layer.layer-type" wdKey="add-layer.layer-type"
onChange={(v: string) => this.setState({ type: v })} onChange={(v: LayerSpecification["type"]) => this.setState({ type: v })}
/> />
{this.state.type !== 'background' && {this.state.type !== 'background' &&
<FieldSource <FieldSource
+8 -8
View File
@@ -6,16 +6,16 @@ import Modal from './Modal'
type ModalDebugProps = { type ModalDebugProps = {
isOpen: boolean isOpen: boolean
renderer: string renderer: string
onChangeMaboxGlDebug(...args: unknown[]): unknown onChangeMaplibreGlDebug(key: string, checked: boolean): unknown
onChangeOpenlayersDebug(...args: unknown[]): unknown onChangeOpenlayersDebug(key: string, checked: boolean): unknown
onOpenToggle(...args: unknown[]): unknown onOpenToggle(value: boolean): unknown
maplibreGlDebugOptions?: object maplibreGlDebugOptions?: object
openlayersDebugOptions?: object openlayersDebugOptions?: object
mapView: { mapView: {
zoom: number zoom: number
center: { center: {
lng: string lng: number
lat: string lat: number
} }
} }
}; };
@@ -26,8 +26,8 @@ export default class ModalDebug extends React.Component<ModalDebugProps> {
const {mapView} = this.props; const {mapView} = this.props;
const osmZoom = Math.round(mapView.zoom)+1; const osmZoom = Math.round(mapView.zoom)+1;
const osmLon = Number.parseFloat(mapView.center.lng).toFixed(5); const osmLon = +(mapView.center.lng).toFixed(5);
const osmLat = Number.parseFloat(mapView.center.lat).toFixed(5); const osmLat = +(mapView.center.lat).toFixed(5);
return <Modal return <Modal
data-wd-key="modal:debug" data-wd-key="modal:debug"
@@ -42,7 +42,7 @@ export default class ModalDebug extends React.Component<ModalDebugProps> {
{Object.entries(this.props.maplibreGlDebugOptions!).map(([key, val]) => { {Object.entries(this.props.maplibreGlDebugOptions!).map(([key, val]) => {
return <li key={key}> return <li key={key}>
<label> <label>
<input type="checkbox" checked={val} onChange={(e) => this.props.onChangeMaboxGlDebug(key, e.target.checked)} /> {key} <input type="checkbox" checked={val} onChange={(e) => this.props.onChangeMaplibreGlDebug(key, e.target.checked)} /> {key}
</label> </label>
</li> </li>
})} })}
+2 -1
View File
@@ -2,7 +2,8 @@ import React from 'react'
import Slugify from 'slugify' import Slugify from 'slugify'
import {saveAs} from 'file-saver' import {saveAs} from 'file-saver'
import {version} from 'maplibre-gl/package.json' import {version} from 'maplibre-gl/package.json'
import {StyleSpecification, format} from '@maplibre/maplibre-gl-style-spec' import {format} from '@maplibre/maplibre-gl-style-spec'
import type {StyleSpecification} from 'maplibre-gl'
import {MdFileDownload} from 'react-icons/md' import {MdFileDownload} from 'react-icons/md'
import FieldString from './FieldString' import FieldString from './FieldString'
+2 -1
View File
@@ -1,5 +1,6 @@
import React from 'react' import React from 'react'
import {LightSpecification, StyleSpecification, TransitionSpecification, latest} from '@maplibre/maplibre-gl-style-spec' import {latest} from '@maplibre/maplibre-gl-style-spec'
import type {LightSpecification, StyleSpecification, TransitionSpecification} from 'maplibre-gl'
import FieldArray from './FieldArray' import FieldArray from './FieldArray'
import FieldNumber from './FieldNumber' import FieldNumber from './FieldNumber'
+4 -2
View File
@@ -1,5 +1,8 @@
import React from 'react' import React from 'react'
import {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, StyleSpecification, VectorSourceSpecification, latest} from '@maplibre/maplibre-gl-style-spec' import {MdAddCircleOutline, MdDelete} from 'react-icons/md'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import type {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, StyleSpecification, VectorSourceSpecification} from 'maplibre-gl'
import Modal from './Modal' import Modal from './Modal'
import InputButton from './InputButton' import InputButton from './InputButton'
import FieldString from './FieldString' import FieldString from './FieldString'
@@ -10,7 +13,6 @@ import style from '../libs/style'
import { deleteSource, addSource, changeSource } from '../libs/source' import { deleteSource, addSource, changeSource } from '../libs/source'
import publicSources from '../config/tilesets.json' import publicSources from '../config/tilesets.json'
import {MdAddCircleOutline, MdDelete} from 'react-icons/md'
type PublicSourceProps = { type PublicSourceProps = {
id: string id: string
+2 -1
View File
@@ -1,7 +1,8 @@
import React from 'react' import React from 'react'
import FieldFunction from './FieldFunction' import FieldFunction from './FieldFunction'
import { LayerSpecification } from '@maplibre/maplibre-gl-style-spec' import type {LayerSpecification} from 'maplibre-gl'
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image'] const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
/** Extract field spec by {@fieldName} from the {@layerType} in the /** Extract field spec by {@fieldName} from the {@layerType} in the
+1
View File
@@ -13,6 +13,7 @@ const typeMap = {
number: () => Block, number: () => Block,
string: () => Block, string: () => Block,
formatted: () => Block, formatted: () => Block,
padding: () => Block,
}; };
export type SpecFieldProps = InputFieldSpecProps & { export type SpecFieldProps = InputFieldSpecProps & {
+2 -2
View File
@@ -31,10 +31,11 @@ function setStopRefs(props: ZoomPropertyProps, state: ZoomPropertyState) {
newRefs = {...state}; newRefs = {...state};
} }
newRefs[idx] = docUid("stop-"); newRefs[idx] = docUid("stop-");
} else {
newRefs[idx] = state.refs[idx];
} }
}) })
} }
return newRefs; return newRefs;
} }
@@ -156,7 +157,6 @@ export default class ZoomProperty extends React.Component<ZoomPropertyProps, Zoo
const key = this.state.refs[idx]; const key = this.state.refs[idx];
const value = stop[1] const value = stop[1]
const deleteStopBtn= <DeleteStopButton onClick={this.props.onDeleteStop?.bind(this, idx)} /> const deleteStopBtn= <DeleteStopButton onClick={this.props.onDeleteStop?.bind(this, idx)} />
return <tr return <tr
key={key} key={key}
> >
+4 -3
View File
@@ -1,10 +1,11 @@
import style from './style.js' import style from './style.js'
import {StyleSpecification, format} from '@maplibre/maplibre-gl-style-spec' import {format} from '@maplibre/maplibre-gl-style-spec'
import type {StyleSpecification} from 'maplibre-gl'
import ReconnectingWebSocket from 'reconnecting-websocket' import ReconnectingWebSocket from 'reconnecting-websocket'
export type ApiStyleStoreOptions = { export type ApiStyleStoreOptions = {
port?: string port: string | null
host?: string host: string | null
onLocalStyleChange?: (style: any) => void onLocalStyleChange?: (style: any) => void
} }
+2 -1
View File
@@ -1,4 +1,5 @@
import {StyleSpecification, diff} from '@maplibre/maplibre-gl-style-spec' import {diff} from '@maplibre/maplibre-gl-style-spec'
import type {StyleSpecification} from 'maplibre-gl'
function diffMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) { function diffMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) {
const changes = diff(beforeStyle, afterStyle) const changes = diff(beforeStyle, afterStyle)
+1 -1
View File
@@ -2,7 +2,7 @@
import stylegen from 'mapbox-gl-inspect/lib/stylegen' import stylegen from 'mapbox-gl-inspect/lib/stylegen'
// @ts-ignore // @ts-ignore
import colors from 'mapbox-gl-inspect/lib/colors' import colors from 'mapbox-gl-inspect/lib/colors'
import {FilterSpecification,LayerSpecification } from '@maplibre/maplibre-gl-style-spec' import type {FilterSpecification,LayerSpecification } from 'maplibre-gl'
export type HighlightedLayer = LayerSpecification & {filter?: FilterSpecification}; export type HighlightedLayer = LayerSpecification & {filter?: FilterSpecification};
+3 -3
View File
@@ -1,7 +1,7 @@
import type {StyleSpecification} from "@maplibre/maplibre-gl-style-spec"; import type {StyleSpecification} from "maplibre-gl";
export class RevisionStore { export class RevisionStore {
revisions: StyleSpecification[]; revisions: (StyleSpecification & {id: string})[];
currentIdx: number; currentIdx: number;
@@ -18,7 +18,7 @@ export class RevisionStore {
return this.revisions[this.currentIdx] return this.revisions[this.currentIdx]
} }
addRevision(revision: StyleSpecification) { addRevision(revision: StyleSpecification & {id: string}) {
//TODO: compare new revision style id with old ones //TODO: compare new revision style id with old ones
//and ensure that it is always the same id //and ensure that it is always the same id
this.revisions.push(revision) this.revisions.push(revision)
+1 -1
View File
@@ -1,4 +1,4 @@
import type {StyleSpecification, SourceSpecification} from "@maplibre/maplibre-gl-style-spec"; import type {StyleSpecification, SourceSpecification} from "maplibre-gl";
export function deleteSource(mapStyle: StyleSpecification, sourceId: string) { export function deleteSource(mapStyle: StyleSpecification, sourceId: string) {
const remainingSources = { ...mapStyle.sources} const remainingSources = { ...mapStyle.sources}
+2 -1
View File
@@ -1,4 +1,5 @@
import {derefLayers, StyleSpecification, LayerSpecification} from '@maplibre/maplibre-gl-style-spec' import {derefLayers} from '@maplibre/maplibre-gl-style-spec'
import type {StyleSpecification, LayerSpecification} from 'maplibre-gl'
import tokens from '../config/tokens.json' import tokens from '../config/tokens.json'
// Empty style is always used if no style could be restored or fetched // Empty style is always used if no style could be restored or fetched
+1 -1
View File
@@ -1,7 +1,7 @@
import style from './style' import style from './style'
import {loadStyleUrl} from './urlopen' import {loadStyleUrl} from './urlopen'
import publicSources from '../config/styles.json' import publicSources from '../config/styles.json'
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec' import type {StyleSpecification} from 'maplibre-gl'
const storagePrefix = "maputnik" const storagePrefix = "maputnik"
const stylePrefix = 'style' const stylePrefix = 'style'