More migration of components

This commit is contained in:
HarelM
2023-12-22 00:16:13 +02:00
parent fa182e66fa
commit 12def85771
15 changed files with 268 additions and 242 deletions
+7
View File
@@ -70,6 +70,7 @@
"@types/codemirror": "^5.60.15", "@types/codemirror": "^5.60.15",
"@types/color": "^3.0.6", "@types/color": "^3.0.6",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/file-saver": "^2.0.7",
"@types/lodash.capitalize": "^4.2.9", "@types/lodash.capitalize": "^4.2.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",
@@ -4711,6 +4712,12 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"node_modules/@types/file-saver": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
"integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
"dev": true
},
"node_modules/@types/find-cache-dir": { "node_modules/@types/find-cache-dir": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz",
+1
View File
@@ -99,6 +99,7 @@
"@types/codemirror": "^5.60.15", "@types/codemirror": "^5.60.15",
"@types/color": "^3.0.6", "@types/color": "^3.0.6",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/file-saver": "^2.0.7",
"@types/lodash.capitalize": "^4.2.9", "@types/lodash.capitalize": "^4.2.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",
-20
View File
@@ -1,20 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputCheckbox from './InputCheckbox'
export default class FieldCheckbox extends React.Component {
static propTypes = {
...InputCheckbox.propTypes,
}
render() {
const {props} = this;
return <Block label={this.props.label}>
<InputCheckbox {...props} />
</Block>
}
}
+18
View File
@@ -0,0 +1,18 @@
import React from 'react'
import Block from './Block'
import InputCheckbox, {InputCheckboxProps} from './InputCheckbox'
type FieldCheckboxProps = InputCheckboxProps & {
label?: string;
};
export default class FieldCheckbox extends React.Component<FieldCheckboxProps> {
render() {
return <Block label={this.props.label}>
<InputCheckbox {...this.props} />
</Block>
}
}
-21
View File
@@ -1,21 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import Block from './Block'
import InputDynamicArray from './InputDynamicArray'
import Fieldset from './Fieldset'
export default class FieldDynamicArray extends React.Component {
static propTypes = {
...InputDynamicArray.propTypes,
name: PropTypes.string,
}
render() {
const {props} = this;
return <Fieldset label={props.label}>
<InputDynamicArray {...props} />
</Fieldset>
}
}
+16
View File
@@ -0,0 +1,16 @@
import React from 'react'
import InputDynamicArray, {FieldDynamicArrayProps as InputDynamicArrayProps} from './InputDynamicArray'
import Fieldset from './Fieldset'
type FieldDynamicArrayProps = InputDynamicArrayProps & {
name?: string
};
export default class FieldDynamicArray extends React.Component<FieldDynamicArrayProps> {
render() {
return <Fieldset label={this.props.label}>
<InputDynamicArray {...this.props} />
</Fieldset>
}
}
@@ -1,19 +1,18 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec' import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block' import Block from './Block'
import InputAutocomplete from './InputAutocomplete' import InputAutocomplete from './InputAutocomplete'
export default class FieldSource extends React.Component { type FieldSourceProps = {
static propTypes = { value?: string
value: PropTypes.string, wdKey?: string
wdKey: PropTypes.string, onChange?(...args: unknown[]): unknown
onChange: PropTypes.func, sourceIds?: unknown[]
sourceIds: PropTypes.array, error?: unknown[]
error: PropTypes.object, };
}
export default class FieldSource extends React.Component<FieldSourceProps> {
static defaultProps = { static defaultProps = {
onChange: () => {}, onChange: () => {},
sourceIds: [], sourceIds: [],
@@ -29,7 +28,7 @@ export default class FieldSource extends React.Component {
<InputAutocomplete <InputAutocomplete
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
options={this.props.sourceIds.map(src => [src, src])} options={this.props.sourceIds?.map(src => [src, src])}
/> />
</Block> </Block>
} }
@@ -1,18 +1,17 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec' import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block' import Block from './Block'
import InputAutocomplete from './InputAutocomplete' import InputAutocomplete from './InputAutocomplete'
export default class FieldSourceLayer extends React.Component { type FieldSourceLayerProps = {
static propTypes = { value?: string
value: PropTypes.string, onChange?(...args: unknown[]): unknown
onChange: PropTypes.func, sourceLayerIds?: unknown[]
sourceLayerIds: PropTypes.array, isFixed?: boolean
isFixed: PropTypes.bool, };
}
export default class FieldSourceLayer extends React.Component<FieldSourceLayerProps> {
static defaultProps = { static defaultProps = {
onChange: () => {}, onChange: () => {},
sourceLayerIds: [], sourceLayerIds: [],
@@ -27,7 +26,7 @@ export default class FieldSourceLayer extends React.Component {
keepMenuWithinWindowBounds={!!this.props.isFixed} keepMenuWithinWindowBounds={!!this.props.isFixed}
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
options={this.props.sourceLayerIds.map(l => [l, l])} options={this.props.sourceLayerIds?.map(l => [l, l])}
/> />
</Block> </Block>
} }
+1 -1
View File
@@ -12,7 +12,7 @@ import InputUrl from './InputUrl'
export type FieldDynamicArrayProps = { export type FieldDynamicArrayProps = {
value?: (string | number)[] value?: (string | number)[]
type?: 'url' | 'number' | 'enum' type?: 'url' | 'number' | 'enum' | 'string'
default?: (string | number)[] default?: (string | number)[]
onChange?(...args: unknown[]): unknown onChange?(...args: unknown[]): unknown
style?: object style?: object
+4 -4
View File
@@ -17,7 +17,7 @@ export type InputJsonProps = {
onChange?(...args: unknown[]): unknown onChange?(...args: unknown[]): unknown
lineNumbers?: boolean lineNumbers?: boolean
lineWrapping?: boolean lineWrapping?: boolean
getValue(data: any): string getValue?(data: any): string
gutters?: string[] gutters?: string[]
className?: string className?: string
onFocus?(...args: unknown[]): unknown onFocus?(...args: unknown[]): unknown
@@ -58,13 +58,13 @@ export default class InputJson extends React.Component<InputJsonProps, InputJson
this.state = { this.state = {
isEditing: false, isEditing: false,
showMessage: false, showMessage: false,
prevValue: this.props.getValue(this.props.layer), prevValue: this.props.getValue!(this.props.layer),
}; };
} }
componentDidMount () { componentDidMount () {
this._doc = CodeMirror(this._el!, { this._doc = CodeMirror(this._el!, {
value: this.props.getValue(this.props.layer), value: this.props.getValue!(this.props.layer),
mode: this.props.mode || { mode: this.props.mode || {
name: "mgl", name: "mgl",
}, },
@@ -117,7 +117,7 @@ export default class InputJson extends React.Component<InputJsonProps, InputJson
if (!this.state.isEditing && prevProps.layer !== this.props.layer) { if (!this.state.isEditing && prevProps.layer !== this.props.layer) {
this._cancelNextChange = true; this._cancelNextChange = true;
this._doc!.setValue( this._doc!.setValue(
this.props.getValue(this.props.layer), this.props.getValue!(this.props.layer),
) )
} }
} }
@@ -1,29 +1,32 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import InputButton from './InputButton' import InputButton from './InputButton'
import Modal from './Modal' import Modal from './Modal'
import FieldType from './FieldType' 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'
export default class ModalAdd extends React.Component { type ModalAddProps = {
static propTypes = { layers: unknown[]
layers: PropTypes.array.isRequired, onLayersChange(...args: unknown[]): unknown
onLayersChange: PropTypes.func.isRequired, isOpen: boolean
isOpen: PropTypes.bool.isRequired, onOpenToggle(...args: unknown[]): unknown
onOpenToggle: PropTypes.func.isRequired,
// A dict of source id's and the available source layers // A dict of source id's and the available source layers
sources: PropTypes.object.isRequired, sources: any
} };
type ModalAddState = {
type: string
id: string
source?: string
'source-layer'?: string
};
export default class ModalAdd extends React.Component<ModalAddProps, ModalAddState> {
addLayer = () => { addLayer = () => {
const changedLayers = this.props.layers.slice(0) const changedLayers = this.props.layers.slice(0)
const layer = { const layer: ModalAddState = {
id: this.state.id, id: this.state.id,
type: this.state.type, type: this.state.type,
} }
@@ -41,20 +44,21 @@ export default class ModalAdd extends React.Component {
this.props.onOpenToggle(false) this.props.onOpenToggle(false)
} }
constructor(props) { constructor(props: ModalAddProps) {
super(props) super(props)
this.state = { const state: ModalAddState = {
type: 'fill', type: 'fill',
id: '', id: '',
} }
if(props.sources.length > 0) { if(props.sources.length > 0) {
this.state.source = Object.keys(this.props.sources)[0] state.source = Object.keys(this.props.sources)[0];
this.state['source-layer'] = this.props.sources[this.state.source][0] state['source-layer'] = this.props.sources[state.source as keyof ModalAddProps["sources"]][0]
} }
this.state = state;
} }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(_prevProps: ModalAddProps, prevState: ModalAddState) {
// Check if source is valid for new type // Check if source is valid for new type
const oldType = prevState.type; const oldType = prevState.type;
const newType = this.state.type; const newType = this.state.type;
@@ -67,9 +71,9 @@ export default class ModalAdd extends React.Component {
oldType !== newType oldType !== newType
&& prevState.source !== "" && prevState.source !== ""
// Was a valid source previously // Was a valid source previously
&& availableSourcesOld.indexOf(prevState.source) > -1 && availableSourcesOld.indexOf(prevState.source!) > -1
// And is not a valid source now // And is not a valid source now
&& availableSourcesNew.indexOf(this.state.source) < 0 && availableSourcesNew.indexOf(this.state.source!) < 0
) { ) {
// Clear the source // Clear the source
this.setState({ this.setState({
@@ -78,12 +82,12 @@ export default class ModalAdd extends React.Component {
} }
} }
getLayersForSource(source) { getLayersForSource(source: string) {
const sourceObj = this.props.sources[source] || {}; const sourceObj = this.props.sources[source] || {};
return sourceObj.layers || []; return sourceObj.layers || [];
} }
getSources(type) { getSources(type: string) {
const sources = []; const sources = [];
const types = { const types = {
@@ -108,8 +112,9 @@ export default class ModalAdd extends React.Component {
] ]
} }
for(let [key, val] of Object.entries(this.props.sources)) { for(let [key, val] of Object.entries(this.props.sources) as any) {
if(types[val.type] && types[val.type].indexOf(type) > -1) { const valType = val.type as keyof typeof types;
if(types[valType] && types[valType].indexOf(type) > -1) {
sources.push(key); sources.push(key);
} }
} }
@@ -120,7 +125,7 @@ export default class ModalAdd extends React.Component {
render() { render() {
const sources = this.getSources(this.state.type); const sources = this.getSources(this.state.type);
const layers = this.getLayersForSource(this.state.source); const layers = this.getLayersForSource(this.state.source!);
return <Modal return <Modal
isOpen={this.props.isOpen} isOpen={this.props.isOpen}
@@ -131,25 +136,23 @@ export default class ModalAdd extends React.Component {
> >
<div className="maputnik-add-layer"> <div className="maputnik-add-layer">
<FieldId <FieldId
label="ID"
fieldSpec={latest.layer.id}
value={this.state.id} value={this.state.id}
wdKey="add-layer.layer-id" wdKey="add-layer.layer-id"
onChange={v => { onChange={(v: string) => {
this.setState({ id: v }) this.setState({ id: v })
}} }}
/> />
<FieldType <FieldType
value={this.state.type} value={this.state.type}
wdKey="add-layer.layer-type" wdKey="add-layer.layer-type"
onChange={v => this.setState({ type: v })} onChange={(v: string) => this.setState({ type: v })}
/> />
{this.state.type !== 'background' && {this.state.type !== 'background' &&
<FieldSource <FieldSource
sourceIds={sources} sourceIds={sources}
wdKey="add-layer.layer-source-block" wdKey="add-layer.layer-source-block"
value={this.state.source} value={this.state.source}
onChange={v => this.setState({ source: v })} onChange={(v: string) => this.setState({ source: v })}
/> />
} }
{['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.state.type) < 0 && {['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.state.type) < 0 &&
@@ -157,7 +160,7 @@ export default class ModalAdd extends React.Component {
isFixed={true} isFixed={true}
sourceLayerIds={layers} sourceLayerIds={layers}
value={this.state['source-layer']} value={this.state['source-layer']}
onChange={v => this.setState({ 'source-layer': v })} onChange={(v: string) => this.setState({ 'source-layer': v })}
/> />
} }
<InputButton <InputButton
@@ -1,21 +1,27 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types'
import Modal from './Modal' import Modal from './Modal'
export default class ModalDebug extends React.Component { type ModalDebugProps = {
static propTypes = { isOpen: boolean
isOpen: PropTypes.bool.isRequired, renderer: string
renderer: PropTypes.string.isRequired, onChangeMaboxGlDebug(...args: unknown[]): unknown
onChangeMaboxGlDebug: PropTypes.func.isRequired, onChangeOpenlayersDebug(...args: unknown[]): unknown
onChangeOpenlayersDebug: PropTypes.func.isRequired, onOpenToggle(...args: unknown[]): unknown
onOpenToggle: PropTypes.func.isRequired, maplibreGlDebugOptions?: object
maplibreGlDebugOptions: PropTypes.object, openlayersDebugOptions?: object
openlayersDebugOptions: PropTypes.object, mapView: {
mapView: PropTypes.object, zoom: number
center: {
lng: string
lat: string
} }
}
};
export default class ModalDebug extends React.Component<ModalDebugProps> {
render() { render() {
const {mapView} = this.props; const {mapView} = this.props;
@@ -33,10 +39,10 @@ export default class ModalDebug extends React.Component {
<h1>Options</h1> <h1>Options</h1>
{this.props.renderer === 'mlgljs' && {this.props.renderer === 'mlgljs' &&
<ul> <ul>
{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} onClick={(e) => this.props.onChangeMaboxGlDebug(key, e.target.checked)} /> {key} <input type="checkbox" checked={val} onChange={(e) => this.props.onChangeMaboxGlDebug(key, e.target.checked)} /> {key}
</label> </label>
</li> </li>
})} })}
@@ -44,10 +50,10 @@ export default class ModalDebug extends React.Component {
} }
{this.props.renderer === 'ol' && {this.props.renderer === 'ol' &&
<ul> <ul>
{Object.entries(this.props.openlayersDebugOptions).map(([key, val]) => { {Object.entries(this.props.openlayersDebugOptions!).map(([key, val]) => {
return <li key={key}> return <li key={key}>
<label> <label>
<input type="checkbox" checked={val} onClick={(e) => this.props.onChangeOpenlayersDebug(key, e.target.checked)} /> {key} <input type="checkbox" checked={val} onChange={(e) => this.props.onChangeOpenlayersDebug(key, e.target.checked)} /> {key}
</label> </label>
</li> </li>
})} })}
@@ -1,13 +1,13 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types'
import Slugify from 'slugify' import Slugify from 'slugify'
import {saveAs} from 'file-saver' import {saveAs} from 'file-saver'
import {version} from 'maplibre-gl' import {version} from 'maplibre-gl/package.json'
import {format} from '@maplibre/maplibre-gl-style-spec' import {StyleSpecification, format} from '@maplibre/maplibre-gl-style-spec'
import {MdFileDownload} from 'react-icons/md'
import FieldString from './FieldString' import FieldString from './FieldString'
import InputButton from './InputButton' import InputButton from './InputButton'
import Modal from './Modal' import Modal from './Modal'
import {MdFileDownload} from 'react-icons/md'
import style from '../libs/style' import style from '../libs/style'
import fieldSpecAdditional from '../libs/field-spec-additional' import fieldSpecAdditional from '../libs/field-spec-additional'
@@ -15,17 +15,15 @@ import fieldSpecAdditional from '../libs/field-spec-additional'
const MAPLIBRE_GL_VERSION = version; const MAPLIBRE_GL_VERSION = version;
export default class ModalExport extends React.Component { type ModalExportProps = {
static propTypes = { mapStyle: StyleSpecification & { id: string }
mapStyle: PropTypes.object.isRequired, onStyleChanged(...args: unknown[]): unknown
onStyleChanged: PropTypes.func.isRequired, isOpen: boolean
isOpen: PropTypes.bool.isRequired, onOpenToggle(...args: unknown[]): unknown
onOpenToggle: PropTypes.func.isRequired, };
}
constructor(props) {
super(props); export default class ModalExport extends React.Component<ModalExportProps> {
}
tokenizedStyle () { tokenizedStyle () {
return format( return format(
@@ -88,11 +86,11 @@ export default class ModalExport extends React.Component {
saveAs(blob, exportName + ".json"); saveAs(blob, exportName + ".json");
} }
changeMetadataProperty(property, value) { changeMetadataProperty(property: string, value: any) {
const changedStyle = { const changedStyle = {
...this.props.mapStyle, ...this.props.mapStyle,
metadata: { metadata: {
...this.props.mapStyle.metadata, ...this.props.mapStyle.metadata as any,
[property]: value [property]: value
} }
} }
@@ -119,13 +117,13 @@ export default class ModalExport extends React.Component {
<FieldString <FieldString
label={fieldSpecAdditional.maputnik.maptiler_access_token.label} label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token} fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']} value={(this.props.mapStyle.metadata || {} as any)['maputnik:openmaptiles_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")} onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
/> />
<FieldString <FieldString
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label} label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token} fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
value={(this.props.mapStyle.metadata || {})['maputnik:thunderforest_access_token']} value={(this.props.mapStyle.metadata || {} as any)['maputnik:thunderforest_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")} onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
/> />
</div> </div>
@@ -1,12 +1,10 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, StyleSpecification, VectorSourceSpecification, latest} from '@maplibre/maplibre-gl-style-spec'
import {latest} from '@maplibre/maplibre-gl-style-spec'
import Modal from './Modal' import Modal from './Modal'
import InputButton from './InputButton' import InputButton from './InputButton'
import Block from './Block'
import FieldString from './FieldString' import FieldString from './FieldString'
import FieldSelect from './FieldSelect' import FieldSelect from './FieldSelect'
import ModalSourcesTypeEditor from './ModalSourcesTypeEditor' import ModalSourcesTypeEditor, { EditorMode } from './ModalSourcesTypeEditor'
import style from '../libs/style' import style from '../libs/style'
import { deleteSource, addSource, changeSource } from '../libs/source' import { deleteSource, addSource, changeSource } from '../libs/source'
@@ -14,14 +12,14 @@ import publicSources from '../config/tilesets.json'
import {MdAddCircleOutline, MdDelete} from 'react-icons/md' import {MdAddCircleOutline, MdDelete} from 'react-icons/md'
class PublicSource extends React.Component { type PublicSourceProps = {
static propTypes = { id: string
id: PropTypes.string.isRequired, type: string
type: PropTypes.string.isRequired, title: string
title: PropTypes.string.isRequired, onSelect(...args: unknown[]): unknown
onSelect: PropTypes.func.isRequired, };
}
class PublicSource extends React.Component<PublicSourceProps> {
render() { render() {
return <div className="maputnik-public-source"> return <div className="maputnik-public-source">
<InputButton <InputButton
@@ -39,7 +37,7 @@ class PublicSource extends React.Component {
} }
} }
function editorMode(source) { function editorMode(source: SourceSpecification) {
if(source.type === 'raster') { if(source.type === 'raster') {
if(source.tiles) return 'tilexyz_raster' if(source.tiles) return 'tilexyz_raster'
return 'tilejson_raster' return 'tilejson_raster'
@@ -69,16 +67,15 @@ function editorMode(source) {
return null return null
} }
class ActiveModalSourcesTypeEditor extends React.Component { type ActiveModalSourcesTypeEditorProps = {
static propTypes = { sourceId: string
sourceId: PropTypes.string.isRequired, source: SourceSpecification
source: PropTypes.object.isRequired, onDelete(...args: unknown[]): unknown
onDelete: PropTypes.func.isRequired, onChange(...args: unknown[]): unknown
onChange: PropTypes.func.isRequired, };
}
class ActiveModalSourcesTypeEditor extends React.Component<ActiveModalSourcesTypeEditorProps> {
render() { render() {
const inputProps = { }
return <div className="maputnik-active-source-type-editor"> return <div className="maputnik-active-source-type-editor">
<div className="maputnik-active-source-type-editor-header"> <div className="maputnik-active-source-type-editor-header">
<span className="maputnik-active-source-type-editor-header-id">#{this.props.sourceId}</span> <span className="maputnik-active-source-type-editor-header-id">#{this.props.sourceId}</span>
@@ -103,12 +100,18 @@ class ActiveModalSourcesTypeEditor extends React.Component {
} }
} }
class AddSource extends React.Component { type AddSourceProps = {
static propTypes = { onAdd(...args: unknown[]): unknown
onAdd: PropTypes.func.isRequired, };
}
constructor(props) { type AddSourceState = {
mode: EditorMode
sourceId: string
source: SourceSpecification
};
class AddSource extends React.Component<AddSourceProps, AddSourceState> {
constructor(props: AddSourceProps) {
super(props) super(props)
this.state = { this.state = {
mode: 'tilejson_vector', mode: 'tilejson_vector',
@@ -117,7 +120,7 @@ class AddSource extends React.Component {
} }
} }
defaultSource(mode) { defaultSource(mode: EditorMode): SourceSpecification {
const source = (this.state || {}).source || {} const source = (this.state || {}).source || {}
const {protocol} = window.location; const {protocol} = window.location;
@@ -128,38 +131,38 @@ class AddSource extends React.Component {
} }
case 'geojson_json': return { case 'geojson_json': return {
type: 'geojson', type: 'geojson',
cluster: source.cluster || false, cluster: (source as GeoJSONSourceSpecification).cluster || false,
data: {} data: {}
} }
case 'tilejson_vector': return { case 'tilejson_vector': return {
type: 'vector', type: 'vector',
url: source.url || `${protocol}//localhost:3000/tilejson.json` url: (source as VectorSourceSpecification).url || `${protocol}//localhost:3000/tilejson.json`
} }
case 'tilexyz_vector': return { case 'tilexyz_vector': return {
type: 'vector', type: 'vector',
tiles: source.tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`], tiles: (source as VectorSourceSpecification).tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`],
minZoom: source.minzoom || 0, minzoom: (source as VectorSourceSpecification).minzoom || 0,
maxZoom: source.maxzoom || 14 maxzoom: (source as VectorSourceSpecification).maxzoom || 14
} }
case 'tilejson_raster': return { case 'tilejson_raster': return {
type: 'raster', type: 'raster',
url: source.url || `${protocol}//localhost:3000/tilejson.json` url: (source as RasterSourceSpecification).url || `${protocol}//localhost:3000/tilejson.json`
} }
case 'tilexyz_raster': return { case 'tilexyz_raster': return {
type: 'raster', type: 'raster',
tiles: source.tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`], tiles: (source as RasterSourceSpecification).tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`],
minzoom: source.minzoom || 0, minzoom: (source as RasterSourceSpecification).minzoom || 0,
maxzoom: source.maxzoom || 14 maxzoom: (source as RasterSourceSpecification).maxzoom || 14
} }
case 'tilejson_raster-dem': return { case 'tilejson_raster-dem': return {
type: 'raster-dem', type: 'raster-dem',
url: source.url || `${protocol}//localhost:3000/tilejson.json` url: (source as RasterDEMSourceSpecification).url || `${protocol}//localhost:3000/tilejson.json`
} }
case 'tilexyz_raster-dem': return { case 'tilexyz_raster-dem': return {
type: 'raster-dem', type: 'raster-dem',
tiles: source.tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`], tiles: (source as RasterDEMSourceSpecification).tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`],
minzoom: source.minzoom || 0, minzoom: (source as RasterDEMSourceSpecification).minzoom || 0,
maxzoom: source.maxzoom || 14 maxzoom: (source as RasterDEMSourceSpecification).maxzoom || 14
} }
case 'image': return { case 'image': return {
type: 'image', type: 'image',
@@ -183,7 +186,7 @@ class AddSource extends React.Component {
[0,0], [0,0],
], ],
} }
default: return {} default: return {} as any
} }
} }
@@ -192,7 +195,7 @@ class AddSource extends React.Component {
this.props.onAdd(sourceId, source); this.props.onAdd(sourceId, source);
} }
onChangeSource = (source) => { onChangeSource = (source: SourceSpecification) => {
this.setState({source}); this.setState({source});
} }
@@ -213,7 +216,7 @@ class AddSource extends React.Component {
label={"Source ID"} label={"Source ID"}
fieldSpec={{doc: "Unique ID that identifies the source and is used in the layer to reference the source."}} fieldSpec={{doc: "Unique ID that identifies the source and is used in the layer to reference the source."}}
value={this.state.sourceId} value={this.state.sourceId}
onChange={v => this.setState({ sourceId: v})} onChange={(v: string) => this.setState({ sourceId: v})}
/> />
<FieldSelect <FieldSelect
label={"Source Type"} label={"Source Type"}
@@ -230,8 +233,8 @@ class AddSource extends React.Component {
['image', 'Image'], ['image', 'Image'],
['video', 'Video'], ['video', 'Video'],
]} ]}
onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})} onChange={(mode: EditorMode) => this.setState({mode: mode, source: this.defaultSource(mode)})}
value={this.state.mode} value={this.state.mode as string}
/> />
<ModalSourcesTypeEditor <ModalSourcesTypeEditor
onChange={this.onChangeSource} onChange={this.onChangeSource}
@@ -248,15 +251,15 @@ class AddSource extends React.Component {
} }
} }
export default class ModalSources extends React.Component { type ModalSourcesProps = {
static propTypes = { mapStyle: StyleSpecification
mapStyle: PropTypes.object.isRequired, isOpen: boolean
isOpen: PropTypes.bool.isRequired, onOpenToggle(...args: unknown[]): unknown
onOpenToggle: PropTypes.func.isRequired, onStyleChanged(...args: unknown[]): unknown
onStyleChanged: PropTypes.func.isRequired, };
}
stripTitle(source) { export default class ModalSources extends React.Component<ModalSourcesProps> {
stripTitle(source: SourceSpecification & {title?: string}): SourceSpecification {
const strippedSource = {...source} const strippedSource = {...source}
delete strippedSource['title'] delete strippedSource['title']
return strippedSource return strippedSource
@@ -270,13 +273,13 @@ export default class ModalSources extends React.Component {
key={sourceId} key={sourceId}
sourceId={sourceId} sourceId={sourceId}
source={source} source={source}
onChange={src => this.props.onStyleChanged(changeSource(mapStyle, sourceId, src))} onChange={(src: SourceSpecification) => this.props.onStyleChanged(changeSource(mapStyle, sourceId, src))}
onDelete={() => this.props.onStyleChanged(deleteSource(mapStyle, sourceId))} onDelete={() => this.props.onStyleChanged(deleteSource(mapStyle, sourceId))}
/> />
}) })
const tilesetOptions = Object.keys(publicSources).filter(sourceId => !(sourceId in mapStyle.sources)).map(sourceId => { const tilesetOptions = Object.keys(publicSources).filter((sourceId: string) => !(sourceId in mapStyle.sources)).map((sourceId: string) => {
const source = publicSources[sourceId] const source = publicSources[sourceId as keyof typeof publicSources] as SourceSpecification & {title: string};
return <PublicSource return <PublicSource
key={sourceId} key={sourceId}
id={sourceId} id={sourceId}
@@ -286,7 +289,6 @@ export default class ModalSources extends React.Component {
/> />
}) })
const inputProps = { }
return <Modal return <Modal
data-wd-key="modal:sources" data-wd-key="modal:sources"
isOpen={this.props.isOpen} isOpen={this.props.isOpen}
@@ -303,7 +305,7 @@ export default class ModalSources extends React.Component {
<p> <p>
Add one of the publicly available sources to your style. Add one of the publicly available sources to your style.
</p> </p>
<div className="maputnik-public-sources" style={{maxwidth: 500}}> <div className="maputnik-public-sources" style={{maxWidth: 500}}>
{tilesetOptions} {tilesetOptions}
</div> </div>
</section> </section>
@@ -312,7 +314,7 @@ export default class ModalSources extends React.Component {
<h1>Add New Source</h1> <h1>Add New Source</h1>
<p>Add a new source to your style. You can only choose the source type and id at creation time!</p> <p>Add a new source to your style. You can only choose the source type and id at creation time!</p>
<AddSource <AddSource
onAdd={(sourceId, source) => this.props.onStyleChanged(addSource(mapStyle, sourceId, source))} onAdd={(sourceId: string, source: SourceSpecification) => this.props.onStyleChanged(addSource(mapStyle, sourceId, source))}
/> />
</section> </section>
</Modal> </Modal>
@@ -1,5 +1,4 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@maplibre/maplibre-gl-style-spec' import {latest} from '@maplibre/maplibre-gl-style-spec'
import Block from './Block' import Block from './Block'
import FieldUrl from './FieldUrl' import FieldUrl from './FieldUrl'
@@ -10,14 +9,18 @@ import FieldArray from './FieldArray'
import FieldJson from './FieldJson' import FieldJson from './FieldJson'
import FieldCheckbox from './FieldCheckbox' import FieldCheckbox from './FieldCheckbox'
export type EditorMode = "video" | "image" | "tilejson_vector" | "tilexyz_raster" | "tilejson_raster" | "tilexyz_raster-dem" | "tilejson_raster-dem" | "tilexyz_vector" | "geojson_url" | "geojson_json" | null;
class TileJSONSourceEditor extends React.Component { type TileJSONSourceEditorProps = {
static propTypes = { source: {
source: PropTypes.object.isRequired, url: string
onChange: PropTypes.func.isRequired,
children: PropTypes.node,
} }
onChange(...args: unknown[]): unknown
children?: React.ReactNode
};
class TileJSONSourceEditor extends React.Component<TileJSONSourceEditorProps> {
render() { render() {
return <div> return <div>
<FieldUrl <FieldUrl
@@ -34,14 +37,18 @@ class TileJSONSourceEditor extends React.Component {
} }
} }
class TileURLSourceEditor extends React.Component { type TileURLSourceEditorProps = {
static propTypes = { source: {
source: PropTypes.object.isRequired, tiles: string[]
onChange: PropTypes.func.isRequired, minzoom: number
children: PropTypes.node, maxzoom: number
} }
onChange(...args: unknown[]): unknown
children?: React.ReactNode
};
changeTileUrls(tiles) { class TileURLSourceEditor extends React.Component<TileURLSourceEditorProps> {
changeTileUrls(tiles: string[]) {
this.props.onChange({ this.props.onChange({
...this.props.source, ...this.props.source,
tiles, tiles,
@@ -86,14 +93,17 @@ class TileURLSourceEditor extends React.Component {
} }
} }
class ImageSourceEditor extends React.Component { type ImageSourceEditorProps = {
static propTypes = { source: {
source: PropTypes.object.isRequired, coordinates: [number, number][]
onChange: PropTypes.func.isRequired, url: string
} }
onChange(...args: unknown[]): unknown
};
class ImageSourceEditor extends React.Component<ImageSourceEditorProps> {
render() { render() {
const changeCoord = (idx, val) => { const changeCoord = (idx: number, val: [number, number]) => {
const coordinates = this.props.source.coordinates.slice(0); const coordinates = this.props.source.coordinates.slice(0);
coordinates[idx] = val; coordinates[idx] = val;
@@ -122,7 +132,7 @@ class ImageSourceEditor extends React.Component {
type="number" type="number"
value={this.props.source.coordinates[idx]} value={this.props.source.coordinates[idx]}
default={[0, 0]} default={[0, 0]}
onChange={(val) => changeCoord(idx, val)} onChange={(val: [number, number]) => changeCoord(idx, val)}
/> />
); );
})} })}
@@ -130,14 +140,17 @@ class ImageSourceEditor extends React.Component {
} }
} }
class VideoSourceEditor extends React.Component { type VideoSourceEditorProps = {
static propTypes = { source: {
source: PropTypes.object.isRequired, coordinates: [number, number][]
onChange: PropTypes.func.isRequired, urls: string[]
} }
onChange(...args: unknown[]): unknown
};
class VideoSourceEditor extends React.Component<VideoSourceEditorProps> {
render() { render() {
const changeCoord = (idx, val) => { const changeCoord = (idx: number, val: [number, number]) => {
const coordinates = this.props.source.coordinates.slice(0); const coordinates = this.props.source.coordinates.slice(0);
coordinates[idx] = val; coordinates[idx] = val;
@@ -147,7 +160,7 @@ class VideoSourceEditor extends React.Component {
}); });
} }
const changeUrls = (urls) => { const changeUrls = (urls: string[]) => {
this.props.onChange({ this.props.onChange({
...this.props.source, ...this.props.source,
urls, urls,
@@ -160,7 +173,7 @@ class VideoSourceEditor extends React.Component {
fieldSpec={latest.source_video.urls} fieldSpec={latest.source_video.urls}
type="string" type="string"
value={this.props.source.urls} value={this.props.source.urls}
default={""} default={[]}
onChange={changeUrls} onChange={changeUrls}
/> />
{["top left", "top right", "bottom right", "bottom left"].map((label, idx) => { {["top left", "top right", "bottom right", "bottom left"].map((label, idx) => {
@@ -172,7 +185,7 @@ class VideoSourceEditor extends React.Component {
type="number" type="number"
value={this.props.source.coordinates[idx]} value={this.props.source.coordinates[idx]}
default={[0, 0]} default={[0, 0]}
onChange={val => changeCoord(idx, val)} onChange={(val: [number, number]) => changeCoord(idx, val)}
/> />
); );
})} })}
@@ -180,12 +193,14 @@ class VideoSourceEditor extends React.Component {
} }
} }
class GeoJSONSourceUrlEditor extends React.Component { type GeoJSONSourceUrlEditorProps = {
static propTypes = { source: {
source: PropTypes.object.isRequired, data: string
onChange: PropTypes.func.isRequired,
} }
onChange(...args: unknown[]): unknown
};
class GeoJSONSourceUrlEditor extends React.Component<GeoJSONSourceUrlEditorProps> {
render() { render() {
return <FieldUrl return <FieldUrl
label={"GeoJSON URL"} label={"GeoJSON URL"}
@@ -199,12 +214,15 @@ class GeoJSONSourceUrlEditor extends React.Component {
} }
} }
class GeoJSONSourceFieldJsonEditor extends React.Component { type GeoJSONSourceFieldJsonEditorProps = {
static propTypes = { source: {
source: PropTypes.object.isRequired, data: any,
onChange: PropTypes.func.isRequired, cluster: boolean
} }
onChange(...args: unknown[]): unknown
};
class GeoJSONSourceFieldJsonEditor extends React.Component<GeoJSONSourceFieldJsonEditorProps> {
render() { render() {
return <div> return <div>
<Block label={"GeoJSON"} fieldSpec={latest.source_geojson.data}> <Block label={"GeoJSON"} fieldSpec={latest.source_geojson.data}>
@@ -238,13 +256,13 @@ class GeoJSONSourceFieldJsonEditor extends React.Component {
} }
} }
export default class ModalSourcesTypeEditor extends React.Component { type ModalSourcesTypeEditorProps = {
static propTypes = { mode: EditorMode
mode: PropTypes.string.isRequired, source: any
source: PropTypes.object.isRequired, onChange(...args: unknown[]): unknown
onChange: PropTypes.func.isRequired, };
}
export default class ModalSourcesTypeEditor extends React.Component<ModalSourcesTypeEditorProps> {
render() { render() {
const commonProps = { const commonProps = {
source: this.props.source, source: this.props.source,