mirror of
https://github.com/maputnik/editor.git
synced 2026-08-24 05:57:25 +00:00
Migrate all the non react components code to typescript (#847)
This completes the migration to typescript of all the non react components code. The only changes introduced besides types are the type checks using `"something" in object` which narrows down types in typescript.
This commit is contained in:
Generated
+10
@@ -68,6 +68,7 @@
|
|||||||
"@storybook/react-vite": "^7.6.5",
|
"@storybook/react-vite": "^7.6.5",
|
||||||
"@storybook/theming": "^7.6.5",
|
"@storybook/theming": "^7.6.5",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
|
"@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",
|
||||||
"@types/react-dom": "^16.9.24",
|
"@types/react-dom": "^16.9.24",
|
||||||
@@ -4702,6 +4703,15 @@
|
|||||||
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
|
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash.isequal": {
|
||||||
|
"version": "4.5.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz",
|
||||||
|
"integrity": "sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/lodash.throttle": {
|
"node_modules/@types/lodash.throttle": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.9.tgz",
|
||||||
|
|||||||
@@ -97,6 +97,7 @@
|
|||||||
"@storybook/react-vite": "^7.6.5",
|
"@storybook/react-vite": "^7.6.5",
|
||||||
"@storybook/theming": "^7.6.5",
|
"@storybook/theming": "^7.6.5",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
|
"@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",
|
||||||
"@types/react-dom": "^16.9.24",
|
"@types/react-dom": "^16.9.24",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import InputUrl from './InputUrl'
|
|||||||
import {MdFileUpload} from 'react-icons/md'
|
import {MdFileUpload} from 'react-icons/md'
|
||||||
import {MdAddCircleOutline} from 'react-icons/md'
|
import {MdAddCircleOutline} from 'react-icons/md'
|
||||||
|
|
||||||
import style from '../libs/style.js'
|
import style from '../libs/style'
|
||||||
import publicStyles from '../config/styles.json'
|
import publicStyles from '../config/styles.json'
|
||||||
|
|
||||||
class PublicStyle extends React.Component {
|
class PublicStyle extends React.Component {
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
import style from './style.js'
|
import style from './style.js'
|
||||||
import {format} from '@maplibre/maplibre-gl-style-spec'
|
import {StyleSpecification, format} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||||
|
|
||||||
|
export type ApiStyleStoreOptions = {
|
||||||
|
port?: string
|
||||||
|
host?: string
|
||||||
|
onLocalStyleChange?: (style: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
export class ApiStyleStore {
|
export class ApiStyleStore {
|
||||||
|
|
||||||
constructor(opts) {
|
localUrl: string;
|
||||||
|
websocketUrl: string;
|
||||||
|
latestStyleId: string | undefined = undefined;
|
||||||
|
onLocalStyleChange: (style: any) => void;
|
||||||
|
|
||||||
|
constructor(opts: ApiStyleStoreOptions) {
|
||||||
this.onLocalStyleChange = opts.onLocalStyleChange || (() => {})
|
this.onLocalStyleChange = opts.onLocalStyleChange || (() => {})
|
||||||
const port = opts.port || '8000'
|
const port = opts.port || '8000'
|
||||||
const host = opts.host || 'localhost'
|
const host = opts.host || 'localhost'
|
||||||
@@ -13,7 +24,7 @@ export class ApiStyleStore {
|
|||||||
this.init = this.init.bind(this)
|
this.init = this.init.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb) {
|
init(cb: (...args: any[]) => void) {
|
||||||
fetch(this.localUrl + '/styles', {
|
fetch(this.localUrl + '/styles', {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
})
|
})
|
||||||
@@ -26,7 +37,7 @@ export class ApiStyleStore {
|
|||||||
this.notifyLocalChanges()
|
this.notifyLocalChanges()
|
||||||
cb(null)
|
cb(null)
|
||||||
})
|
})
|
||||||
.catch(function(e) {
|
.catch(() => {
|
||||||
cb(new Error('Can not connect to style API'))
|
cb(new Error('Can not connect to style API'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -47,7 +58,7 @@ export class ApiStyleStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
latestStyle(cb) {
|
latestStyle(cb: (...args: any[]) => void) {
|
||||||
if(this.latestStyleId) {
|
if(this.latestStyleId) {
|
||||||
fetch(this.localUrl + '/styles/' + this.latestStyleId, {
|
fetch(this.localUrl + '/styles/' + this.latestStyleId, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
@@ -64,7 +75,7 @@ export class ApiStyleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save current style replacing previous version
|
// Save current style replacing previous version
|
||||||
save(mapStyle) {
|
save(mapStyle: StyleSpecification & { id: string }) {
|
||||||
const styleJSON = format(
|
const styleJSON = format(
|
||||||
style.stripAccessTokens(
|
style.stripAccessTokens(
|
||||||
style.replaceAccessTokens(mapStyle)
|
style.replaceAccessTokens(mapStyle)
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
|
// @ts-ignore
|
||||||
import stylegen from 'mapbox-gl-inspect/lib/stylegen'
|
import stylegen from 'mapbox-gl-inspect/lib/stylegen'
|
||||||
|
// @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'
|
||||||
|
|
||||||
export function colorHighlightedLayer(layer) {
|
export function colorHighlightedLayer(layer: LayerSpecification) {
|
||||||
if(!layer || layer.type === 'background' || layer.type === 'raster') return null
|
if(!layer || layer.type === 'background' || layer.type === 'raster') return null
|
||||||
|
|
||||||
function changeLayer(l) {
|
function changeLayer(l: LayerSpecification & {filter?: FilterSpecification}) {
|
||||||
if(l.type === 'circle') {
|
if(l.type === 'circle') {
|
||||||
l.paint['circle-radius'] = 3
|
l.paint!['circle-radius'] = 3
|
||||||
} else if(l.type === 'line') {
|
} else if(l.type === 'line') {
|
||||||
l.paint['line-width'] = 2
|
l.paint!['line-width'] = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
if(layer.filter) {
|
if("filter" in layer) {
|
||||||
l.filter = layer.filter
|
l.filter = layer.filter
|
||||||
} else {
|
} else {
|
||||||
delete l['filter']
|
delete l['filter']
|
||||||
@@ -21,8 +24,7 @@ export function colorHighlightedLayer(layer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sourceLayerId = layer['source-layer'] || ''
|
const sourceLayerId = layer['source-layer'] || ''
|
||||||
const color = colors.brightColor(sourceLayerId, 1)
|
const color = colors.brightColor(sourceLayerId, 1);
|
||||||
const layers = []
|
|
||||||
|
|
||||||
if(layer.type === "fill" || layer.type === 'fill-extrusion') {
|
if(layer.type === "fill" || layer.type === 'fill-extrusion') {
|
||||||
return changeLayer(stylegen.polygonLayer(color, color, layer.source, layer['source-layer']))
|
return changeLayer(stylegen.polygonLayer(color, color, layer.source, layer['source-layer']))
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
import {latest} from '@maplibre/maplibre-gl-style-spec'
|
||||||
|
import { LayerSpecification } from 'maplibre-gl'
|
||||||
|
|
||||||
export function changeType(layer, newType) {
|
export function changeType(layer: LayerSpecification, newType: string) {
|
||||||
const changedPaintProps = { ...layer.paint }
|
const changedPaintProps: LayerSpecification["paint"] = { ...layer.paint }
|
||||||
Object.keys(changedPaintProps).forEach(propertyName => {
|
Object.keys(changedPaintProps).forEach(propertyName => {
|
||||||
if(!(propertyName in latest['paint_' + newType])) {
|
if(!(propertyName in latest['paint_' + newType])) {
|
||||||
delete changedPaintProps[propertyName]
|
delete changedPaintProps[propertyName as keyof LayerSpecification["paint"]]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const changedLayoutProps = { ...layer.layout }
|
const changedLayoutProps: LayerSpecification["layout"] = { ...layer.layout }
|
||||||
Object.keys(changedLayoutProps).forEach(propertyName => {
|
Object.keys(changedLayoutProps).forEach(propertyName => {
|
||||||
if(!(propertyName in latest['layout_' + newType])) {
|
if(!(propertyName in latest['layout_' + newType])) {
|
||||||
delete changedLayoutProps[propertyName]
|
delete changedLayoutProps[propertyName as keyof LayerSpecification["layout"]]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -26,15 +27,15 @@ export function changeType(layer, newType) {
|
|||||||
/** A {@property} in either the paint our layout {@group} has changed
|
/** A {@property} in either the paint our layout {@group} has changed
|
||||||
* to a {@newValue}.
|
* to a {@newValue}.
|
||||||
*/
|
*/
|
||||||
export function changeProperty(layer, group, property, newValue) {
|
export function changeProperty(layer: LayerSpecification, group: keyof LayerSpecification, property: string, newValue: any) {
|
||||||
// Remove the property if undefined
|
// Remove the property if undefined
|
||||||
if(newValue === undefined) {
|
if(newValue === undefined) {
|
||||||
if(group) {
|
if(group) {
|
||||||
const newLayer = {
|
const newLayer: any = {
|
||||||
...layer,
|
...layer,
|
||||||
// Change object so the diff works in ./src/components/map/MaplibreGlMap.jsx
|
// Change object so the diff works in ./src/components/map/MaplibreGlMap.jsx
|
||||||
[group]: {
|
[group]: {
|
||||||
...layer[group]
|
...layer[group] as any
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
delete newLayer[group][property];
|
delete newLayer[group][property];
|
||||||
@@ -45,7 +46,7 @@ export function changeProperty(layer, group, property, newValue) {
|
|||||||
}
|
}
|
||||||
return newLayer;
|
return newLayer;
|
||||||
} else {
|
} else {
|
||||||
const newLayer = {
|
const newLayer: any = {
|
||||||
...layer
|
...layer
|
||||||
};
|
};
|
||||||
delete newLayer[property];
|
delete newLayer[property];
|
||||||
@@ -57,7 +58,7 @@ export function changeProperty(layer, group, property, newValue) {
|
|||||||
return {
|
return {
|
||||||
...layer,
|
...layer,
|
||||||
[group]: {
|
[group]: {
|
||||||
...layer[group],
|
...layer[group] as any,
|
||||||
[property]: newValue
|
[property]: newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,22 @@
|
|||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
import isEqual from 'lodash.isequal'
|
import isEqual from 'lodash.isequal'
|
||||||
|
import { Map } from 'maplibre-gl';
|
||||||
|
|
||||||
|
export type LayerWatcherOptions = {
|
||||||
|
onSourcesChange?: (sources: { [sourceId: string]: string[] }) => void;
|
||||||
|
onVectorLayersChange?: (vectorLayers: { [vectorLayerId: string]: { [propertyName: string]: { [propertyValue: string]: {} } } }) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/** Listens to map events to build up a store of available vector
|
/** Listens to map events to build up a store of available vector
|
||||||
* layers contained in the tiles */
|
* layers contained in the tiles */
|
||||||
export default class LayerWatcher {
|
export default class LayerWatcher {
|
||||||
constructor(opts = {}) {
|
onSourcesChange: (sources: { [sourceId: string]: string[] }) => void;
|
||||||
|
onVectorLayersChange: (vectorLayers: { [vectorLayerId: string]: { [propertyName: string]: { [propertyValue: string]: {} } } }) => void;
|
||||||
|
throttledAnalyzeVectorLayerFields: (map: any) => void;
|
||||||
|
_sources: { [sourceId: string]: string[] };
|
||||||
|
_vectorLayers: { [vectorLayerId: string]: { [propertyName: string]: { [propertyValue: string]: {} } } };
|
||||||
|
|
||||||
|
constructor(opts: LayerWatcherOptions = {}) {
|
||||||
this.onSourcesChange = opts.onSourcesChange || (() => {})
|
this.onSourcesChange = opts.onSourcesChange || (() => {})
|
||||||
this.onVectorLayersChange = opts.onVectorLayersChange || (() => {})
|
this.onVectorLayersChange = opts.onVectorLayersChange || (() => {})
|
||||||
|
|
||||||
@@ -17,13 +29,13 @@ export default class LayerWatcher {
|
|||||||
this.throttledAnalyzeVectorLayerFields = throttle(this.analyzeVectorLayerFields, 5000)
|
this.throttledAnalyzeVectorLayerFields = throttle(this.analyzeVectorLayerFields, 5000)
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeMap(map) {
|
analyzeMap(map: Map) {
|
||||||
const previousSources = { ...this._sources }
|
const previousSources = { ...this._sources }
|
||||||
|
|
||||||
Object.keys(map.style.sourceCaches).forEach(sourceId => {
|
Object.keys(map.style.sourceCaches).forEach(sourceId => {
|
||||||
//NOTE: This heavily depends on the internal API of Maplibre GL
|
//NOTE: This heavily depends on the internal API of Maplibre GL
|
||||||
//so this breaks between Maplibre GL JS releases
|
//so this breaks between Maplibre GL JS releases
|
||||||
this._sources[sourceId] = map.style.sourceCaches[sourceId]._source.vectorLayerIds
|
this._sources[sourceId] = map.style.sourceCaches[sourceId]._source.vectorLayerIds as string[];
|
||||||
})
|
})
|
||||||
|
|
||||||
if(!isEqual(previousSources, this._sources)) {
|
if(!isEqual(previousSources, this._sources)) {
|
||||||
@@ -33,14 +45,14 @@ export default class LayerWatcher {
|
|||||||
this.throttledAnalyzeVectorLayerFields(map)
|
this.throttledAnalyzeVectorLayerFields(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeVectorLayerFields(map) {
|
analyzeVectorLayerFields(map: Map) {
|
||||||
const previousVectorLayers = { ...this._vectorLayers }
|
const previousVectorLayers = { ...this._vectorLayers }
|
||||||
|
|
||||||
Object.keys(this._sources).forEach(sourceId => {
|
Object.keys(this._sources).forEach(sourceId => {
|
||||||
(this._sources[sourceId] || []).forEach(vectorLayerId => {
|
(this._sources[sourceId] || []).forEach(vectorLayerId => {
|
||||||
const knownProperties = this._vectorLayers[vectorLayerId] || {}
|
const knownProperties = this._vectorLayers[vectorLayerId] || {}
|
||||||
const params = { sourceLayer: vectorLayerId }
|
const params = { sourceLayer: vectorLayerId }
|
||||||
map.querySourceFeatures(sourceId, params).forEach(feature => {
|
map.querySourceFeatures(sourceId, params as any).forEach(feature => {
|
||||||
Object.keys(feature.properties).forEach(propertyName => {
|
Object.keys(feature.properties).forEach(propertyName => {
|
||||||
const knownPropertyValues = knownProperties[propertyName] || {}
|
const knownPropertyValues = knownProperties[propertyName] || {}
|
||||||
knownPropertyValues[feature.properties[propertyName]] = {}
|
knownPropertyValues[feature.properties[propertyName]] = {}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
import MapLibreGl from "maplibre-gl"
|
import MapLibreGl from "maplibre-gl"
|
||||||
|
|
||||||
MapLibreGl.setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js');
|
MapLibreGl.setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', () => {});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import npmurl from 'url'
|
import npmurl from 'url'
|
||||||
|
|
||||||
function loadJSON(url, defaultValue, cb) {
|
function loadJSON(url: string, defaultValue: any, cb: (...args: any[]) => void) {
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
credentials: "same-origin"
|
credentials: "same-origin"
|
||||||
@@ -17,7 +17,7 @@ function loadJSON(url, defaultValue, cb) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadGlyphsMetadata(urlTemplate, cb) {
|
export function downloadGlyphsMetadata(urlTemplate: string, cb: (...args: any[]) => void) {
|
||||||
if(!urlTemplate) return cb([])
|
if(!urlTemplate) return cb([])
|
||||||
|
|
||||||
// Special handling because Tileserver GL serves the fontstacks metadata differently
|
// Special handling because Tileserver GL serves the fontstacks metadata differently
|
||||||
@@ -27,14 +27,14 @@ export function downloadGlyphsMetadata(urlTemplate, cb) {
|
|||||||
if(urlObj.pathname === normPathPart) {
|
if(urlObj.pathname === normPathPart) {
|
||||||
urlObj.pathname = '/fontstacks.json';
|
urlObj.pathname = '/fontstacks.json';
|
||||||
} else {
|
} else {
|
||||||
urlObj.pathname = urlObj.pathname.replace(normPathPart, '.json');
|
urlObj.pathname = urlObj.pathname!.replace(normPathPart, '.json');
|
||||||
}
|
}
|
||||||
let url = npmurl.format(urlObj);
|
let url = npmurl.format(urlObj);
|
||||||
|
|
||||||
loadJSON(url, [], cb)
|
loadJSON(url, [], cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadSpriteMetadata(baseUrl, cb) {
|
export function downloadSpriteMetadata(baseUrl: string, cb: (...args: any[]) => void) {
|
||||||
if(!baseUrl) return cb([])
|
if(!baseUrl) return cb([])
|
||||||
const url = baseUrl + '.json'
|
const url = baseUrl + '.json'
|
||||||
loadJSON(url, {}, glyphs => cb(Object.keys(glyphs)))
|
loadJSON(url, {}, glyphs => cb(Object.keys(glyphs)))
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
|
import type {StyleSpecification} from "@maplibre/maplibre-gl-style-spec";
|
||||||
|
|
||||||
export class RevisionStore {
|
export class RevisionStore {
|
||||||
|
revisions: StyleSpecification[];
|
||||||
|
currentIdx: number;
|
||||||
|
|
||||||
|
|
||||||
constructor(initialRevisions=[]) {
|
constructor(initialRevisions=[]) {
|
||||||
this.revisions = initialRevisions
|
this.revisions = initialRevisions
|
||||||
this.currentIdx = initialRevisions.length - 1
|
this.currentIdx = initialRevisions.length - 1
|
||||||
@@ -12,7 +18,7 @@ export class RevisionStore {
|
|||||||
return this.revisions[this.currentIdx]
|
return this.revisions[this.currentIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
addRevision(revision) {
|
addRevision(revision: StyleSpecification) {
|
||||||
//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)
|
||||||
@@ -21,15 +27,15 @@ export class RevisionStore {
|
|||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
if(this.currentIdx > 0) {
|
if(this.currentIdx > 0) {
|
||||||
this.currentIdx--
|
this.currentIdx--;
|
||||||
}
|
}
|
||||||
return this.current
|
return this.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
redo() {
|
redo() {
|
||||||
if(this.currentIdx < this.revisions.length - 1) {
|
if(this.currentIdx < this.revisions.length - 1) {
|
||||||
this.currentIdx++
|
this.currentIdx++
|
||||||
}
|
}
|
||||||
return this.current
|
return this.current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
export function deleteSource(mapStyle, sourceId) {
|
|
||||||
const remainingSources = { ...mapStyle.sources}
|
|
||||||
delete remainingSources[sourceId]
|
|
||||||
return {
|
|
||||||
...mapStyle,
|
|
||||||
sources: remainingSources
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function addSource(mapStyle, sourceId, source) {
|
|
||||||
return changeSource(mapStyle, sourceId, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function changeSource(mapStyle, sourceId, source) {
|
|
||||||
const changedSources = {
|
|
||||||
...mapStyle.sources,
|
|
||||||
[sourceId]: source
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...mapStyle,
|
|
||||||
sources: changedSources
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type {StyleSpecification, SourceSpecification} from "@maplibre/maplibre-gl-style-spec";
|
||||||
|
|
||||||
|
export function deleteSource(mapStyle: StyleSpecification, sourceId: string) {
|
||||||
|
const remainingSources = { ...mapStyle.sources}
|
||||||
|
delete remainingSources[sourceId]
|
||||||
|
return {
|
||||||
|
...mapStyle,
|
||||||
|
sources: remainingSources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function addSource(mapStyle: StyleSpecification, sourceId: string, source: SourceSpecification) {
|
||||||
|
return changeSource(mapStyle, sourceId, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeSource(mapStyle: StyleSpecification, sourceId: string, source: SourceSpecification) {
|
||||||
|
const changedSources = {
|
||||||
|
...mapStyle.sources,
|
||||||
|
[sourceId]: source
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...mapStyle,
|
||||||
|
sources: changedSources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {derefLayers} from '@maplibre/maplibre-gl-style-spec'
|
import {derefLayers, StyleSpecification, LayerSpecification} from '@maplibre/maplibre-gl-style-spec'
|
||||||
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
|
||||||
@@ -9,18 +9,20 @@ const emptyStyle = ensureStyleValidity({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function generateId() {
|
function generateId() {
|
||||||
return Math.random().toString(36).substr(2, 9)
|
return Math.random().toString(36).substring(2, 9)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasId(style) {
|
function ensureHasId(style: StyleSpecification & { id?: string }): StyleSpecification & { id: string } {
|
||||||
if('id' in style) return style
|
if(!('id' in style) || !style.id) {
|
||||||
style.id = generateId()
|
style.id = generateId();
|
||||||
return style
|
return style as StyleSpecification & { id: string };
|
||||||
|
}
|
||||||
|
return style as StyleSpecification & { id: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasNoInteractive(style) {
|
function ensureHasNoInteractive(style: StyleSpecification & {id: string}) {
|
||||||
const changedLayers = style.layers.map(layer => {
|
const changedLayers = style.layers.map(layer => {
|
||||||
const changedLayer = { ...layer }
|
const changedLayer: LayerSpecification & { interactive?: any } = { ...layer }
|
||||||
delete changedLayer.interactive
|
delete changedLayer.interactive
|
||||||
return changedLayer
|
return changedLayer
|
||||||
})
|
})
|
||||||
@@ -31,18 +33,18 @@ function ensureHasNoInteractive(style) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasNoRefs(style) {
|
function ensureHasNoRefs(style: StyleSpecification & {id: string}) {
|
||||||
return {
|
return {
|
||||||
...style,
|
...style,
|
||||||
layers: derefLayers(style.layers)
|
layers: derefLayers(style.layers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureStyleValidity(style) {
|
function ensureStyleValidity(style: StyleSpecification): StyleSpecification & { id: string } {
|
||||||
return ensureHasNoInteractive(ensureHasNoRefs(ensureHasId(style)))
|
return ensureHasNoInteractive(ensureHasNoRefs(ensureHasId(style)))
|
||||||
}
|
}
|
||||||
|
|
||||||
function indexOfLayer(layers, layerId) {
|
function indexOfLayer(layers: LayerSpecification[], layerId: string) {
|
||||||
for (let i = 0; i < layers.length; i++) {
|
for (let i = 0; i < layers.length; i++) {
|
||||||
if(layers[i].id === layerId) {
|
if(layers[i].id === layerId) {
|
||||||
return i
|
return i
|
||||||
@@ -51,25 +53,25 @@ function indexOfLayer(layers, layerId) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAccessToken(sourceName, mapStyle, opts) {
|
function getAccessToken(sourceName: string, mapStyle: StyleSpecification, opts: {allowFallback?: boolean}) {
|
||||||
if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") {
|
if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") {
|
||||||
sourceName = "thunderforest"
|
sourceName = "thunderforest"
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadata = mapStyle.metadata || {}
|
const metadata = mapStyle.metadata || {} as any;
|
||||||
let accessToken = metadata[`maputnik:${sourceName}_access_token`]
|
let accessToken = metadata[`maputnik:${sourceName}_access_token`]
|
||||||
|
|
||||||
if(opts.allowFallback && !accessToken) {
|
if(opts.allowFallback && !accessToken) {
|
||||||
accessToken = tokens[sourceName]
|
accessToken = tokens[sourceName as keyof typeof tokens]
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceSourceAccessToken(mapStyle, sourceName, opts={}) {
|
function replaceSourceAccessToken(mapStyle: StyleSpecification, sourceName: string, opts={}) {
|
||||||
const source = mapStyle.sources[sourceName]
|
const source = mapStyle.sources[sourceName]
|
||||||
if(!source) return mapStyle
|
if(!source) return mapStyle
|
||||||
if(!source.hasOwnProperty("url")) return mapStyle
|
if(!("url" in source) || !source.url) return mapStyle
|
||||||
|
|
||||||
const accessToken = getAccessToken(sourceName, mapStyle, opts)
|
const accessToken = getAccessToken(sourceName, mapStyle, opts)
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ function replaceSourceAccessToken(mapStyle, sourceName, opts={}) {
|
|||||||
return changedStyle
|
return changedStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceAccessTokens(mapStyle, opts={}) {
|
function replaceAccessTokens(mapStyle: StyleSpecification, opts={}) {
|
||||||
let changedStyle = mapStyle
|
let changedStyle = mapStyle
|
||||||
|
|
||||||
Object.keys(mapStyle.sources).forEach((sourceName) => {
|
Object.keys(mapStyle.sources).forEach((sourceName) => {
|
||||||
@@ -112,9 +114,9 @@ function replaceAccessTokens(mapStyle, opts={}) {
|
|||||||
return changedStyle
|
return changedStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
function stripAccessTokens(mapStyle) {
|
function stripAccessTokens(mapStyle: StyleSpecification) {
|
||||||
const changedMetadata = {
|
const changedMetadata = {
|
||||||
...mapStyle.metadata
|
...mapStyle.metadata as any
|
||||||
};
|
};
|
||||||
delete changedMetadata['maputnik:openmaptiles_access_token'];
|
delete changedMetadata['maputnik:openmaptiles_access_token'];
|
||||||
return {
|
return {
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import style from './style.js'
|
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'
|
||||||
|
|
||||||
const storagePrefix = "maputnik"
|
const storagePrefix = "maputnik"
|
||||||
const stylePrefix = 'style'
|
const stylePrefix = 'style'
|
||||||
@@ -12,7 +13,7 @@ const storageKeys = {
|
|||||||
const defaultStyleUrl = publicSources[0].url
|
const defaultStyleUrl = publicSources[0].url
|
||||||
|
|
||||||
// Fetch a default style via URL and return it or a fallback style via callback
|
// Fetch a default style via URL and return it or a fallback style via callback
|
||||||
export function loadDefaultStyle(cb) {
|
export function loadDefaultStyle(cb: (...args: any[]) => void) {
|
||||||
loadStyleUrl(defaultStyleUrl, cb)
|
loadStyleUrl(defaultStyleUrl, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,20 +22,20 @@ function loadStoredStyles() {
|
|||||||
const styles = []
|
const styles = []
|
||||||
for (let i = 0; i < window.localStorage.length; i++) {
|
for (let i = 0; i < window.localStorage.length; i++) {
|
||||||
const key = window.localStorage.key(i)
|
const key = window.localStorage.key(i)
|
||||||
if(isStyleKey(key)) {
|
if(isStyleKey(key!)) {
|
||||||
styles.push(fromKey(key))
|
styles.push(fromKey(key!))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return styles
|
return styles
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStyleKey(key) {
|
function isStyleKey(key: string) {
|
||||||
const parts = key.split(":")
|
const parts = key.split(":")
|
||||||
return parts.length === 3 && parts[0] === storagePrefix && parts[1] === stylePrefix
|
return parts.length === 3 && parts[0] === storagePrefix && parts[1] === stylePrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load style id from key
|
// Load style id from key
|
||||||
function fromKey(key) {
|
function fromKey(key: string) {
|
||||||
if(!isStyleKey(key)) {
|
if(!isStyleKey(key)) {
|
||||||
throw "Key is not a valid style key"
|
throw "Key is not a valid style key"
|
||||||
}
|
}
|
||||||
@@ -45,26 +46,31 @@ function fromKey(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate key that identifies the style with a version
|
// Calculate key that identifies the style with a version
|
||||||
function styleKey(styleId) {
|
function styleKey(styleId: string) {
|
||||||
return [storagePrefix, stylePrefix, styleId].join(":")
|
return [storagePrefix, stylePrefix, styleId].join(":")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manages many possible styles that are stored in the local storage
|
// Manages many possible styles that are stored in the local storage
|
||||||
export class StyleStore {
|
export class StyleStore {
|
||||||
|
/**
|
||||||
|
* List of style ids
|
||||||
|
*/
|
||||||
|
mapStyles: string[];
|
||||||
|
|
||||||
// Tile store will load all items from local storage and
|
// Tile store will load all items from local storage and
|
||||||
// assume they do not change will working on it
|
// assume they do not change will working on it
|
||||||
constructor() {
|
constructor() {
|
||||||
this.mapStyles = loadStoredStyles()
|
this.mapStyles = loadStoredStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb) {
|
init(cb: (...args: any[]) => void) {
|
||||||
cb(null)
|
cb(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete entire style history
|
// Delete entire style history
|
||||||
purge() {
|
purge() {
|
||||||
for (let i = 0; i < window.localStorage.length; i++) {
|
for (let i = 0; i < window.localStorage.length; i++) {
|
||||||
const key = window.localStorage.key(i)
|
const key = window.localStorage.key(i) as string;
|
||||||
if(key.startsWith(storagePrefix)) {
|
if(key.startsWith(storagePrefix)) {
|
||||||
window.localStorage.removeItem(key)
|
window.localStorage.removeItem(key)
|
||||||
}
|
}
|
||||||
@@ -72,9 +78,9 @@ export class StyleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the last edited style
|
// Find the last edited style
|
||||||
latestStyle(cb) {
|
latestStyle(cb: (...args: any[]) => void) {
|
||||||
if(this.mapStyles.length === 0) return loadDefaultStyle(cb)
|
if(this.mapStyles.length === 0) return loadDefaultStyle(cb)
|
||||||
const styleId = window.localStorage.getItem(storageKeys.latest)
|
const styleId = window.localStorage.getItem(storageKeys.latest) as string;
|
||||||
const styleItem = window.localStorage.getItem(styleKey(styleId))
|
const styleItem = window.localStorage.getItem(styleKey(styleId))
|
||||||
|
|
||||||
if(styleItem) return cb(JSON.parse(styleItem))
|
if(styleItem) return cb(JSON.parse(styleItem))
|
||||||
@@ -82,7 +88,7 @@ export class StyleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save current style replacing previous version
|
// Save current style replacing previous version
|
||||||
save(mapStyle) {
|
save(mapStyle: StyleSpecification & { id: string }) {
|
||||||
mapStyle = style.ensureStyleValidity(mapStyle)
|
mapStyle = style.ensureStyleValidity(mapStyle)
|
||||||
const key = styleKey(mapStyle.id)
|
const key = styleKey(mapStyle.id)
|
||||||
window.localStorage.setItem(key, JSON.stringify(mapStyle))
|
window.localStorage.setItem(key, JSON.stringify(mapStyle))
|
||||||
Reference in New Issue
Block a user