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:
Harel M
2023-12-21 00:07:53 +02:00
committed by GitHub
parent e8d07fa694
commit 3bf0e510e6
15 changed files with 149 additions and 96 deletions
+10
View File
@@ -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",
+1
View File
@@ -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",
+1 -1
View File
@@ -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 {
+17 -6
View File
@@ -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']))
+11 -10
View File
@@ -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;
} }
} }
-25
View File
@@ -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
}
}
+27
View File
@@ -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
}
}
+21 -19
View File
@@ -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))