mirror of
https://github.com/maputnik/editor.git
synced 2026-09-01 09:57:27 +00:00
Compare commits
2 Commits
a324ddb654
...
09a1f3f87b
| Author | SHA1 | Date | |
|---|---|---|---|
| 09a1f3f87b | |||
| a22476cab2 |
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2020": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react/jsx-runtime",
|
||||
"plugin:react-hooks/recommended",
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"dist"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"settings": {
|
||||
"react": { "version": "16.4" }
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"react-refresh"],
|
||||
"rules": {
|
||||
"react-refresh/only-export-components": [
|
||||
"warn",
|
||||
{ "allowConstantExport": true }
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{ "argsIgnorePattern": "^_" }
|
||||
],
|
||||
"no-unused-vars": "off",
|
||||
"react/prop-types": ["off"],
|
||||
// Disable no-undef. It's covered by @typescript-eslint
|
||||
"no-undef": "off",
|
||||
"indent": ["error", 2]
|
||||
},
|
||||
"globals": {
|
||||
"global": "readonly"
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
settings: { react: { version: '18.2' } },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -42,6 +42,9 @@ jobs:
|
||||
${{ runner.os }}-node-
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- run: npm run lint
|
||||
- run: npm run lint-css
|
||||
|
||||
|
||||
|
||||
build-artifacts:
|
||||
|
||||
+3
-9
@@ -6,17 +6,11 @@ COPY package.json package-lock.json ./
|
||||
RUN npm install
|
||||
|
||||
# Build maputnik
|
||||
# TODO: we should also do a npm run test here (needs more dependencies)
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Create a clean nginx-alpine slim image with just the build results
|
||||
FROM nginx:alpine-slim
|
||||
|
||||
# Create a clean python-based image with just the build results
|
||||
FROM python:3-slim
|
||||
WORKDIR /maputnik
|
||||
|
||||
COPY --from=builder /maputnik/dist .
|
||||
|
||||
EXPOSE 8888
|
||||
CMD python -m http.server 8888
|
||||
COPY --from=builder /maputnik/dist /usr/share/nginx/html/
|
||||
|
||||
Generated
+244
-139
File diff suppressed because it is too large
Load Diff
+6
-3
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint ./src --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0 && npm run lint-css",
|
||||
"lint": "eslint ./src --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"test": "cypress run",
|
||||
"cy:open": "cypress open",
|
||||
"lint-css": "stylelint \"src/styles/*.scss\"",
|
||||
@@ -26,6 +26,8 @@
|
||||
"@maplibre/maplibre-gl-style-spec": "^17.0.1",
|
||||
"@mdi/js": "^6.6.96",
|
||||
"@mdi/react": "^1.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.16.0",
|
||||
"@typescript-eslint/parser": "^6.16.0",
|
||||
"array-move": "^4.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"classnames": "^2.3.1",
|
||||
@@ -103,6 +105,7 @@
|
||||
"@types/color": "^3.0.6",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/json-to-ast": "^2.1.4",
|
||||
"@types/lodash.capitalize": "^4.2.9",
|
||||
"@types/lodash.clamp": "^4.0.9",
|
||||
@@ -124,10 +127,10 @@
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"cors": "^2.8.5",
|
||||
"cypress": "^13.6.1",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"express": "^4.17.3",
|
||||
"istanbul": "^0.4.5",
|
||||
"istanbul-lib-coverage": "^3.2.0",
|
||||
|
||||
+13
-13
@@ -334,7 +334,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
const metadata: {[key: string]: string} = this.state.mapStyle.metadata || {} as any
|
||||
const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles
|
||||
|
||||
let glyphUrl = (typeof urlTemplate === 'string')? urlTemplate.replace('{key}', accessToken): urlTemplate;
|
||||
const glyphUrl = (typeof urlTemplate === 'string')? urlTemplate.replace('{key}', accessToken): urlTemplate;
|
||||
downloadGlyphsMetadata(glyphUrl, fonts => {
|
||||
this.setState({ spec: updateRootSpec(this.state.spec, 'glyphs', fonts)})
|
||||
})
|
||||
@@ -402,7 +402,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
// Special case: Duplicate layer id
|
||||
const dupMatch = error.message.match(/layers\[(\d+)\]: (duplicate layer id "?(.*)"?, previously used)/);
|
||||
if (dupMatch) {
|
||||
const [_matchStr, index, message] = dupMatch;
|
||||
const [, index, message] = dupMatch;
|
||||
return {
|
||||
message: error.message,
|
||||
parsed: {
|
||||
@@ -419,7 +419,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
// Special case: Invalid source
|
||||
const invalidSourceMatch = error.message.match(/layers\[(\d+)\]: (source "(?:.*)" not found)/);
|
||||
if (invalidSourceMatch) {
|
||||
const [_matchStr, index, message] = invalidSourceMatch;
|
||||
const [, index, message] = invalidSourceMatch;
|
||||
return {
|
||||
message: error.message,
|
||||
parsed: {
|
||||
@@ -435,7 +435,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
|
||||
const layerMatch = error.message.match(/layers\[(\d+)\]\.(?:(\S+)\.)?(\S+): (.*)/);
|
||||
if (layerMatch) {
|
||||
const [_matchStr, index, group, property, message] = layerMatch;
|
||||
const [, index, group, property, message] = layerMatch;
|
||||
const key = (group && property) ? [group, property].join(".") : property;
|
||||
return {
|
||||
message: error.message,
|
||||
@@ -466,7 +466,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
try {
|
||||
const objPath = message.split(":")[0];
|
||||
// Errors can be deply nested for example 'layers[0].filter[1][1][0]' we only care upto the property 'layers[0].filter'
|
||||
const unsetPath = objPath.match(/^\S+?\[\d+\]\.[^\[]+/)![0];
|
||||
const unsetPath = objPath.match(/^\S+?\[\d+\]\.[^[]+/)![0];
|
||||
unset(dirtyMapStyle, unsetPath);
|
||||
}
|
||||
catch (err) {
|
||||
@@ -547,14 +547,14 @@ export default class App extends React.Component<any, AppState> {
|
||||
}
|
||||
|
||||
onLayerDestroy = (index: number) => {
|
||||
let layers = this.state.mapStyle.layers;
|
||||
const layers = this.state.mapStyle.layers;
|
||||
const remainingLayers = layers.slice(0);
|
||||
remainingLayers.splice(index, 1);
|
||||
this.onLayersChange(remainingLayers);
|
||||
}
|
||||
|
||||
onLayerCopy = (index: number) => {
|
||||
let layers = this.state.mapStyle.layers;
|
||||
const layers = this.state.mapStyle.layers;
|
||||
const changedLayers = layers.slice(0)
|
||||
|
||||
const clonedLayer = cloneDeep(changedLayers[index])
|
||||
@@ -564,7 +564,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
}
|
||||
|
||||
onLayerVisibilityToggle = (index: number) => {
|
||||
let layers = this.state.mapStyle.layers;
|
||||
const layers = this.state.mapStyle.layers;
|
||||
const changedLayers = layers.slice(0)
|
||||
|
||||
const layer = { ...changedLayers[index] }
|
||||
@@ -624,11 +624,11 @@ export default class App extends React.Component<any, AppState> {
|
||||
fetchSources() {
|
||||
const sourceList: {[key: string]: any} = {};
|
||||
|
||||
for(let [key, val] of Object.entries(this.state.mapStyle.sources)) {
|
||||
for(const [key, val] of Object.entries(this.state.mapStyle.sources)) {
|
||||
if(
|
||||
!this.state.sources.hasOwnProperty(key) &&
|
||||
!Object.prototype.hasOwnProperty.call(this.state.sources, key) &&
|
||||
val.type === "vector" &&
|
||||
val.hasOwnProperty("url")
|
||||
Object.prototype.hasOwnProperty.call(val, "url")
|
||||
) {
|
||||
sourceList[key] = {
|
||||
type: val.type,
|
||||
@@ -649,7 +649,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
|
||||
if(!json.hasOwnProperty("vector_layers")) {
|
||||
if(!Object.prototype.hasOwnProperty.call(json, "vector_layers")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -658,7 +658,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
[key]: this.state.sources[key],
|
||||
});
|
||||
|
||||
for(let layer of json.vector_layers) {
|
||||
for(const layer of json.vector_layers) {
|
||||
(sources[key] as any).layers.push(layer.id)
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export default class Doc extends React.Component<DocProps> {
|
||||
<tr key={key}>
|
||||
<td>{key}</td>
|
||||
{Object.keys(headers).map((k) => {
|
||||
if (supportObj.hasOwnProperty(k)) {
|
||||
if (Object.prototype.hasOwnProperty.call(supportObj, k)) {
|
||||
return <td key={k}>{supportObj[k as keyof typeof headers]}</td>;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -40,7 +40,7 @@ function isIdentityProperty(value: any) {
|
||||
return (
|
||||
typeof(value) === 'object' &&
|
||||
value.type === "identity" &&
|
||||
value.hasOwnProperty("property")
|
||||
Object.prototype.hasOwnProperty.call(value, "property")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import FieldDocLabel from './FieldDocLabel'
|
||||
import Doc from './Doc'
|
||||
import generateUniqueId from '../libs/document-uid';
|
||||
|
||||
type FieldsetProps = {
|
||||
label?: string,
|
||||
@@ -12,14 +13,12 @@ type FieldsetState = {
|
||||
showDoc: boolean
|
||||
};
|
||||
|
||||
let IDX = 0;
|
||||
|
||||
export default class Fieldset extends React.Component<FieldsetProps, FieldsetState> {
|
||||
_labelId: string;
|
||||
|
||||
constructor (props: FieldsetProps) {
|
||||
super(props);
|
||||
this._labelId = `fieldset_label_${(IDX++)}`;
|
||||
this._labelId = generateUniqueId(`fieldset_label_`);
|
||||
this.state = {
|
||||
showDoc: false,
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import ExpressionProperty from './_ExpressionProperty';
|
||||
|
||||
|
||||
function combiningFilter(props: FilterEditorProps): LegacyFilterSpecification | ExpressionSpecification {
|
||||
let filter = props.filter || ['all'];
|
||||
const filter = props.filter || ['all'];
|
||||
|
||||
if (!Array.isArray(filter)) {
|
||||
return filter;
|
||||
@@ -148,7 +148,7 @@ export default class FilterEditor extends React.Component<FilterEditorProps, Fil
|
||||
}
|
||||
|
||||
makeExpression = () => {
|
||||
let filter = combiningFilter(this.props);
|
||||
const filter = combiningFilter(this.props);
|
||||
this.props.onChange(migrateFilter(filter));
|
||||
this.setState({
|
||||
displaySimpleFilter: false,
|
||||
@@ -205,8 +205,8 @@ export default class FilterEditor extends React.Component<FilterEditorProps, Fil
|
||||
}
|
||||
else if (displaySimpleFilter) {
|
||||
const filter = combiningFilter(this.props);
|
||||
let combiningOp = filter[0];
|
||||
let filters = filter.slice(1) as (LegacyFilterSpecification | ExpressionSpecification)[];
|
||||
const combiningOp = filter[0];
|
||||
const filters = filter.slice(1) as (LegacyFilterSpecification | ExpressionSpecification)[];
|
||||
|
||||
const actions = (
|
||||
<div>
|
||||
@@ -282,7 +282,7 @@ export default class FilterEditor extends React.Component<FilterEditorProps, Fil
|
||||
);
|
||||
}
|
||||
else {
|
||||
let {filter} = this.props;
|
||||
const {filter} = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -110,7 +110,7 @@ export default class InputColor extends React.Component<InputColorProps> {
|
||||
/>
|
||||
</div>
|
||||
|
||||
var swatchStyle = {
|
||||
const swatchStyle = {
|
||||
backgroundColor: this.props.value
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { BaseSyntheticEvent } from 'react'
|
||||
|
||||
let IDX = 0;
|
||||
import generateUniqueId from '../libs/document-uid';
|
||||
|
||||
export type InputNumberProps = {
|
||||
value?: number
|
||||
@@ -32,7 +31,7 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
|
||||
constructor(props: InputNumberProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
uuid: IDX++,
|
||||
uuid: +generateUniqueId(),
|
||||
editing: false,
|
||||
value: props.value,
|
||||
dirtyValue: props.value,
|
||||
@@ -140,7 +139,7 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
|
||||
}
|
||||
else {
|
||||
value = value + (step - snap);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +154,8 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
|
||||
|
||||
render() {
|
||||
if(
|
||||
this.props.hasOwnProperty("min") && this.props.hasOwnProperty("max") &&
|
||||
Object.prototype.hasOwnProperty.call(this.props, "min") &&
|
||||
Object.prototype.hasOwnProperty.call(this.props, "max") &&
|
||||
this.props.min !== undefined && this.props.max !== undefined &&
|
||||
this.props.allowRange
|
||||
) {
|
||||
|
||||
@@ -60,13 +60,14 @@ export default class SpecField extends React.Component<SpecFieldProps> {
|
||||
max={this.props.fieldSpec.maximum}
|
||||
/>
|
||||
)
|
||||
case 'enum':
|
||||
case 'enum': {
|
||||
const options = Object.keys(this.props.fieldSpec.values || []).map(v => [v, capitalize(v)])
|
||||
|
||||
return <InputEnum
|
||||
{...commonProps as Omit<InputEnumProps, "options">}
|
||||
options={options}
|
||||
/>
|
||||
}
|
||||
case 'resolvedImage':
|
||||
case 'formatted':
|
||||
case 'string':
|
||||
|
||||
@@ -46,7 +46,7 @@ export default class InputString extends React.Component<InputStringProps, Input
|
||||
let tag;
|
||||
let classes;
|
||||
|
||||
if(!!this.props.multi) {
|
||||
if(this.props.multi) {
|
||||
tag = "textarea"
|
||||
classes = [
|
||||
"maputnik-string",
|
||||
@@ -60,14 +60,14 @@ export default class InputString extends React.Component<InputStringProps, Input
|
||||
]
|
||||
}
|
||||
|
||||
if(!!this.props.disabled) {
|
||||
if(this.props.disabled) {
|
||||
classes.push("maputnik-string--disabled");
|
||||
}
|
||||
|
||||
return React.createElement(tag, {
|
||||
"aria-label": this.props["aria-label"],
|
||||
"data-wd-key": this.props["data-wd-key"],
|
||||
spellCheck: this.props.hasOwnProperty("spellCheck") ? this.props.spellCheck : !(tag === "input"),
|
||||
spellCheck: Object.prototype.hasOwnProperty.call(this.props, "spellCheck") ? this.props.spellCheck : !(tag === "input"),
|
||||
disabled: this.props.disabled,
|
||||
className: classes.join(" "),
|
||||
style: this.props.style,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, {type JSX} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton'
|
||||
import {Accordion} from 'react-accessible-accordion';
|
||||
@@ -150,7 +150,7 @@ export default class LayerEditor extends React.Component<LayerEditorProps, Layer
|
||||
|
||||
let sourceLayerIds;
|
||||
const layer = this.props.layer as Exclude<LayerSpecification, BackgroundLayerSpecification>;
|
||||
if(this.props.sources.hasOwnProperty(layer.source)) {
|
||||
if(Object.prototype.hasOwnProperty.call(this.props.sources, layer.source)) {
|
||||
sourceLayerIds = (this.props.sources[layer.source] as any).layers;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, {type JSX} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import lodash from 'lodash';
|
||||
|
||||
@@ -8,26 +8,8 @@ import ModalAdd from './ModalAdd'
|
||||
|
||||
import {SortEndHandler, SortableContainer} from 'react-sortable-hoc';
|
||||
import type {LayerSpecification} from 'maplibre-gl';
|
||||
|
||||
function layerPrefix(name: string) {
|
||||
return name.replace(' ', '-').replace('_', '-').split('-')[0]
|
||||
}
|
||||
|
||||
function findClosestCommonPrefix(layers: LayerSpecification[], idx: number) {
|
||||
const currentLayerPrefix = layerPrefix(layers[idx].id)
|
||||
let closestIdx = idx
|
||||
for (let i = idx; i > 0; i--) {
|
||||
const previousLayerPrefix = layerPrefix(layers[i-1].id)
|
||||
if(previousLayerPrefix === currentLayerPrefix) {
|
||||
closestIdx = i - 1
|
||||
} else {
|
||||
return closestIdx
|
||||
}
|
||||
}
|
||||
return closestIdx
|
||||
}
|
||||
|
||||
let UID = 0;
|
||||
import generateUniqueId from '../libs/document-uid';
|
||||
import { findClosestCommonPrefix, layerPrefix } from '../libs/layer';
|
||||
|
||||
type LayerListContainerProps = {
|
||||
layers: LayerSpecification[]
|
||||
@@ -64,7 +46,7 @@ class LayerListContainer extends React.Component<LayerListContainerProps, LayerL
|
||||
collapsedGroups: {},
|
||||
areAllGroupsExpanded: false,
|
||||
keys: {
|
||||
add: UID++,
|
||||
add: +generateUniqueId(),
|
||||
},
|
||||
isOpen: {
|
||||
add: false,
|
||||
@@ -76,7 +58,7 @@ class LayerListContainer extends React.Component<LayerListContainerProps, LayerL
|
||||
this.setState({
|
||||
keys: {
|
||||
...this.state.keys,
|
||||
[modalName]: UID++,
|
||||
[modalName]: +generateUniqueId(),
|
||||
},
|
||||
isOpen: {
|
||||
...this.state.isOpen,
|
||||
@@ -88,7 +70,7 @@ class LayerListContainer extends React.Component<LayerListContainerProps, LayerL
|
||||
toggleLayers = () => {
|
||||
let idx = 0
|
||||
|
||||
let newGroups: {[key:string]: boolean} = {}
|
||||
const newGroups: {[key:string]: boolean} = {}
|
||||
|
||||
this.groupedLayers().forEach(layers => {
|
||||
const groupPrefix = layerPrefix(layers[0].id)
|
||||
@@ -328,6 +310,7 @@ class LayerListContainer extends React.Component<LayerListContainerProps, LayerL
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
const LayerListContainerSortable = SortableContainer((props: LayerListContainerProps) => <LayerListContainer {...props} />)
|
||||
|
||||
type LayerListProps = LayerListContainerProps & {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, {type JSX} from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import MapLibreGl, {LayerSpecification, LngLat, Map, MapOptions, SourceSpecification, StyleSpecification} from 'maplibre-gl'
|
||||
// @ts-ignore
|
||||
|
||||
@@ -48,7 +48,7 @@ function renderFeature(feature: InspectFeature, idx: number) {
|
||||
}
|
||||
|
||||
function removeDuplicatedFeatures(features: InspectFeature[]) {
|
||||
let uniqueFeatures: InspectFeature[] = [];
|
||||
const uniqueFeatures: InspectFeature[] = [];
|
||||
|
||||
features.forEach(feature => {
|
||||
const featureIndex = uniqueFeatures.findIndex(feature2 => {
|
||||
|
||||
@@ -5,10 +5,10 @@ import type {InspectFeature} from './MapMaplibreGlFeaturePropertyPopup';
|
||||
function groupFeaturesBySourceLayer(features: InspectFeature[]) {
|
||||
const sources: {[key: string]: InspectFeature[]} = {}
|
||||
|
||||
let returnedFeatures: {[key: string]: number} = {}
|
||||
const returnedFeatures: {[key: string]: number} = {}
|
||||
|
||||
features.forEach(feature => {
|
||||
if(returnedFeatures.hasOwnProperty(feature.layer.id)) {
|
||||
if(Object.prototype.hasOwnProperty.call(returnedFeatures, feature.layer.id)) {
|
||||
returnedFeatures[feature.layer.id]++
|
||||
|
||||
const featureObject = sources[feature.layer['source-layer']].find((f: InspectFeature) => f.layer.id === feature.layer.id)
|
||||
@@ -42,21 +42,21 @@ class FeatureLayerPopup extends React.Component<FeatureLayerPopupProps> {
|
||||
const paintProps = feature.layer.paint;
|
||||
let propName;
|
||||
|
||||
if(paintProps.hasOwnProperty("text-color") && paintProps["text-color"]) {
|
||||
if(Object.prototype.hasOwnProperty.call(paintProps, "text-color") && paintProps["text-color"]) {
|
||||
propName = "text-color";
|
||||
}
|
||||
else if (paintProps.hasOwnProperty("fill-color") && paintProps["fill-color"]) {
|
||||
else if (Object.prototype.hasOwnProperty.call(paintProps, "fill-color") && paintProps["fill-color"]) {
|
||||
propName = "fill-color";
|
||||
}
|
||||
else if (paintProps.hasOwnProperty("line-color") && paintProps["line-color"]) {
|
||||
else if (Object.prototype.hasOwnProperty.call(paintProps, "line-color") && paintProps["line-color"]) {
|
||||
propName = "line-color";
|
||||
}
|
||||
else if (paintProps.hasOwnProperty("fill-extrusion-color") && paintProps["fill-extrusion-color"]) {
|
||||
else if (Object.prototype.hasOwnProperty.call(paintProps, "fill-extrusion-color") && paintProps["fill-extrusion-color"]) {
|
||||
propName = "fill-extrusion-color";
|
||||
}
|
||||
|
||||
if(propName) {
|
||||
let color = feature.layer.paint[propName];
|
||||
const color = feature.layer.paint[propName];
|
||||
return String(color);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -99,7 +99,7 @@ export default class MapOpenLayers extends React.Component<MapOpenLayersProps, M
|
||||
});
|
||||
|
||||
map.on('pointermove', (evt) => {
|
||||
var coords = toLonLat(evt.coordinate);
|
||||
const coords = toLonLat(evt.coordinate);
|
||||
this.setState({
|
||||
cursor: [
|
||||
coords[0].toFixed(2),
|
||||
|
||||
@@ -113,7 +113,7 @@ export default class ModalAdd extends React.Component<ModalAddProps, ModalAddSta
|
||||
]
|
||||
}
|
||||
|
||||
for(let [key, val] of Object.entries(this.props.sources) as any) {
|
||||
for(const [key, val] of Object.entries(this.props.sources) as any) {
|
||||
const valType = val.type as keyof typeof types;
|
||||
if(types[valType] && types[valType].indexOf(type) > -1) {
|
||||
sources.push(key);
|
||||
|
||||
@@ -135,7 +135,7 @@ export default class ModalOpen extends React.Component<ModalOpenProps, ModalOpen
|
||||
}
|
||||
|
||||
onUpload = (_: any, files: Result[]) => {
|
||||
const [_e, file] = files[0];
|
||||
const [, file] = files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
this.clearError();
|
||||
|
||||
@@ -23,7 +23,7 @@ function setStopRefs(props: DataPropertyProps, state: DataPropertyState) {
|
||||
|
||||
if(props.value && props.value.stops) {
|
||||
props.value.stops.forEach((_val, idx) => {
|
||||
if(!state.refs.hasOwnProperty(idx)) {
|
||||
if(!Object.prototype.hasOwnProperty.call(state.refs, idx)) {
|
||||
if(!newRefs) {
|
||||
newRefs = {...state};
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ function setStopRefs(props: ZoomPropertyProps, state: ZoomPropertyState) {
|
||||
|
||||
if(props.value && (props.value as ZoomWithStops).stops) {
|
||||
(props.value as ZoomWithStops).stops.forEach((_val, idx: number) => {
|
||||
if(!state.refs.hasOwnProperty(idx)) {
|
||||
if(Object.prototype.hasOwnProperty.call(!state.refs, idx)) {
|
||||
if(!newRefs) {
|
||||
newRefs = {...state};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { IconContext } from "react-icons";
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import './favicon.ico'
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ function get(namespace: keyof DebugStore, key: string) {
|
||||
if(!enabled()) {
|
||||
throw genErr();
|
||||
}
|
||||
if(debugStore.hasOwnProperty(namespace)) {
|
||||
if(Object.prototype.hasOwnProperty.call(debugStore, namespace)) {
|
||||
return debugStore[namespace][key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,3 +70,21 @@ export function changeProperty(layer: LayerSpecification, group: keyof LayerSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function layerPrefix(name: string) {
|
||||
return name.replace(' ', '-').replace('_', '-').split('-')[0]
|
||||
}
|
||||
|
||||
export function findClosestCommonPrefix(layers: LayerSpecification[], idx: number) {
|
||||
const currentLayerPrefix = layerPrefix(layers[idx].id)
|
||||
let closestIdx = idx
|
||||
for (let i = idx; i > 0; i--) {
|
||||
const previousLayerPrefix = layerPrefix(layers[i-1].id)
|
||||
if(previousLayerPrefix === currentLayerPrefix) {
|
||||
closestIdx = i - 1
|
||||
} else {
|
||||
return closestIdx
|
||||
}
|
||||
}
|
||||
return closestIdx
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ export function downloadGlyphsMetadata(urlTemplate: string, cb: (...args: any[])
|
||||
|
||||
// Special handling because Tileserver GL serves the fontstacks metadata differently
|
||||
// https://github.com/klokantech/tileserver-gl/pull/104#issuecomment-274444087
|
||||
let urlObj = npmurl.parse(urlTemplate);
|
||||
const urlObj = npmurl.parse(urlTemplate);
|
||||
const normPathPart = '/%7Bfontstack%7D/%7Brange%7D.pbf';
|
||||
if(urlObj.pathname === normPathPart) {
|
||||
urlObj.pathname = '/fontstacks.json';
|
||||
} else {
|
||||
urlObj.pathname = urlObj.pathname!.replace(normPathPart, '.json');
|
||||
}
|
||||
let url = npmurl.format(urlObj);
|
||||
const url = npmurl.format(urlObj);
|
||||
|
||||
loadJSON(url, [], cb)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@use 'sass:color';
|
||||
// MAP
|
||||
.maputnik-map__container {
|
||||
background: white;
|
||||
@@ -110,12 +111,12 @@
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($color-midgray, 12);
|
||||
background-color: color.adjust($color-midgray, $lightness: 12%);
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: darken($color-midgray, 5);
|
||||
background-color: color.adjust($color-midgray, $lightness: -5%);
|
||||
color: $color-midgray;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
|
||||
.maputnik-filter-editor {
|
||||
@extend .clearfix;
|
||||
@extend .clearfix; /* stylelint-disable-line */
|
||||
color: $color-lowgray;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
|
||||
.maputnik-delete-filter {
|
||||
@extend .maputnik-icon-button;
|
||||
@extend .maputnik-icon-button; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
.maputnik-filter-editor-block-action {
|
||||
@@ -79,7 +79,7 @@
|
||||
}
|
||||
|
||||
.maputnik-radio-as-button {
|
||||
@extend .maputnik-button;
|
||||
@extend .maputnik-button; /* stylelint-disable-line */
|
||||
|
||||
border: solid 1px transparent;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@use 'sass:color';
|
||||
//INPUT
|
||||
.maputnik-input {
|
||||
height: 24px;
|
||||
@@ -10,7 +11,7 @@
|
||||
padding-right: $margin-2;
|
||||
border: none;
|
||||
background-color: $color-gray;
|
||||
color: lighten($color-lowgray, 12);
|
||||
color: color.adjust($color-lowgray, $lightness: 12%);
|
||||
|
||||
&:invalid {
|
||||
border: solid 1px #B71C1C;
|
||||
@@ -19,7 +20,7 @@
|
||||
}
|
||||
|
||||
.maputnik-string {
|
||||
@extend .maputnik-input;
|
||||
@extend .maputnik-input; /* stylelint-disable-line */
|
||||
|
||||
&--multi {
|
||||
resize: vertical;
|
||||
@@ -43,12 +44,12 @@
|
||||
}
|
||||
|
||||
.maputnik-number {
|
||||
@extend .maputnik-input;
|
||||
@extend .maputnik-input; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
//COLOR PICKER
|
||||
.maputnik-color {
|
||||
@extend .maputnik-input;
|
||||
@extend .maputnik-input; /* stylelint-disable-line */
|
||||
|
||||
height: 26px;
|
||||
}
|
||||
@@ -95,7 +96,7 @@
|
||||
|
||||
// SELECT
|
||||
.maputnik-select {
|
||||
@extend .maputnik-input;
|
||||
@extend .maputnik-input; /* stylelint-disable-line */
|
||||
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
@@ -117,7 +118,7 @@
|
||||
}
|
||||
|
||||
.maputnik-button-selected {
|
||||
background-color: lighten($color-midgray, 12);
|
||||
background-color: color.adjust($color-midgray, $lightness: 12%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@
|
||||
outline: none;
|
||||
|
||||
&-wrapper {
|
||||
@extend .maputnik-input;
|
||||
@extend .maputnik-input; /* stylelint-disable-line */
|
||||
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
|
||||
+14
-13
@@ -1,3 +1,4 @@
|
||||
@use 'sass:color';
|
||||
// LAYER LIST
|
||||
.maputnik-layer-list {
|
||||
height: 100%;
|
||||
@@ -48,7 +49,7 @@
|
||||
font-weight: 400;
|
||||
color: $color-lowgray;
|
||||
font-size: $font-size-6;
|
||||
border-bottom-color: lighten($color-black, 0.1);
|
||||
border-bottom-color: color.adjust($color-black, $lightness: 0.1%);
|
||||
user-select: none;
|
||||
list-style: none;
|
||||
z-index: 2000;
|
||||
@@ -87,7 +88,7 @@
|
||||
height: 15px;
|
||||
|
||||
svg {
|
||||
fill: darken($color-lowgray, 20);
|
||||
fill: color.adjust($color-lowgray, $lightness: -20%);
|
||||
|
||||
&:hover {
|
||||
fill: $color-white;
|
||||
@@ -101,13 +102,13 @@
|
||||
|
||||
.maputnik-layer-list-item:hover,
|
||||
.maputnik-layer-list-item-selected {
|
||||
background-color: lighten($color-black, 2);
|
||||
background-color: color.adjust($color-black, $lightness: 2%);
|
||||
|
||||
.maputnik-layer-list-icon-action {
|
||||
display: block;
|
||||
|
||||
svg {
|
||||
fill: darken($color-lowgray, 0.5);
|
||||
fill: color.adjust($color-lowgray, $lightness: -0.5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,7 +152,7 @@
|
||||
border: solid 1px transparent;
|
||||
font-size: $font-size-6;
|
||||
color: $color-lowgray;
|
||||
background-color: lighten($color-black, 2);
|
||||
background-color: color.adjust($color-black, $lightness: 2%);
|
||||
user-select: none;
|
||||
padding: $margin-2;
|
||||
|
||||
@@ -185,7 +186,7 @@
|
||||
.maputnik-layer-editor-group {
|
||||
font-weight: bold;
|
||||
font-size: $font-size-5;
|
||||
background-color: lighten($color-black, 2);
|
||||
background-color: color.adjust($color-black, $lightness: 2%);
|
||||
color: $color-white;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
@@ -232,7 +233,7 @@
|
||||
// PROPERTY
|
||||
.maputnik-default-property {
|
||||
.maputnik-input-block-label {
|
||||
color: darken($color-lowgray, 20%);
|
||||
color: color.adjust($color-lowgray, $lightness: -20%);
|
||||
}
|
||||
|
||||
.maputnik-string,
|
||||
@@ -240,8 +241,8 @@
|
||||
.maputnik-color,
|
||||
.maputnik-select,
|
||||
.maputnik-checkbox-wrapper {
|
||||
background-color: darken($color-gray, 2%);
|
||||
color: darken($color-lowgray, 20%);
|
||||
background-color: color.adjust($color-gray, $lightness: -2%);
|
||||
color: color.adjust($color-lowgray, $lightness: -20%);
|
||||
}
|
||||
|
||||
.maputnik-make-zoom-function svg {
|
||||
@@ -249,17 +250,17 @@
|
||||
}
|
||||
|
||||
.maputnik-multibutton .maputnik-button {
|
||||
background-color: darken($color-midgray, 10%);
|
||||
color: darken($color-lowgray, 20%);
|
||||
background-color: color.adjust($color-midgray, $lightness: -10%);
|
||||
color: color.adjust($color-lowgray, $lightness: -20%);
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($color-midgray, 12);
|
||||
background-color: color.adjust($color-midgray, $lightness: 12%);
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
.maputnik-multibutton .maputnik-button-selected {
|
||||
background-color: darken($color-midgray, 2%);
|
||||
background-color: color.adjust($color-midgray, $lightness: -2%);
|
||||
color: $color-lowgray;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,12 +72,12 @@
|
||||
}
|
||||
|
||||
.maputnik-modal-header-space {
|
||||
@extend .maputnik-space;
|
||||
@extend .maputnik-space; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
//OPEN MODAL
|
||||
.maputnik-upload-button {
|
||||
@extend .maputnik-big-button;
|
||||
@extend .maputnik-big-button; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
.maputnik-style-gallery-container {
|
||||
@@ -139,12 +139,12 @@
|
||||
}
|
||||
|
||||
.maputnik-add-layer {
|
||||
@extend .clearfix;
|
||||
@extend .clearfix; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
//ADD MODAL
|
||||
.maputnik-add-layer-button {
|
||||
@extend .maputnik-big-button;
|
||||
@extend .maputnik-big-button; /* stylelint-disable-line */
|
||||
|
||||
margin-right: $margin-3;
|
||||
float: right;
|
||||
@@ -221,7 +221,7 @@
|
||||
}
|
||||
|
||||
.maputnik-add-source {
|
||||
@extend .clearfix;
|
||||
@extend .clearfix; /* stylelint-disable-line */
|
||||
|
||||
.maputnik-input-block-label {
|
||||
width: 30%;
|
||||
@@ -233,7 +233,7 @@
|
||||
}
|
||||
|
||||
.maputnik-add-source-button {
|
||||
@extend .maputnik-big-button;
|
||||
@extend .maputnik-big-button; /* stylelint-disable-line */
|
||||
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@use 'sass:color';
|
||||
// TOOLBAR
|
||||
.maputnik-toolbar {
|
||||
position: fixed;
|
||||
@@ -75,7 +76,7 @@
|
||||
}
|
||||
|
||||
&:hover .maputnik-toolbar-link-wrapper {
|
||||
background-color: lighten($color-midgray, 12);
|
||||
background-color: color.adjust($color-midgray, $lightness: 12%);
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
@@ -93,13 +94,13 @@
|
||||
.maputnik-toolbar-action {
|
||||
background: inherit;
|
||||
border-width: 0;
|
||||
@extend .maputnik-toolbar-link;
|
||||
@extend .maputnik-toolbar-link; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
.maputnik-toolbar-select {
|
||||
background: inherit;
|
||||
border-width: 0;
|
||||
@extend .maputnik-toolbar-link;
|
||||
@extend .maputnik-toolbar-link; /* stylelint-disable-line */
|
||||
|
||||
select {
|
||||
margin-left: 6px;
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
vertical-align: middle;
|
||||
padding: 0 $margin-2 0 0;
|
||||
|
||||
@extend .maputnik-icon-button;
|
||||
@extend .maputnik-icon-button; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
// ZOOM PROPERTY
|
||||
.maputnik-zoom-spec-property {
|
||||
@extend .clearfix;
|
||||
@extend .clearfix; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
.maputnik-zoom-spec-property-label {
|
||||
@@ -49,7 +49,7 @@
|
||||
padding-top: 0;
|
||||
vertical-align: middle;
|
||||
|
||||
@extend .maputnik-icon-button;
|
||||
@extend .maputnik-icon-button; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
.maputnik-add-stop {
|
||||
@@ -65,7 +65,7 @@
|
||||
vertical-align: middle;
|
||||
padding: 0 $margin-2 0 0;
|
||||
|
||||
@extend .maputnik-icon-button;
|
||||
@extend .maputnik-icon-button; /* stylelint-disable-line */
|
||||
}
|
||||
|
||||
.maputnik-data-spec-property {
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.maputnik-data-spec-property {
|
||||
}
|
||||
|
||||
.maputnik-data-fieldset-inner {
|
||||
background: $color-black;
|
||||
border: solid 1px $color-midgray;
|
||||
|
||||
@@ -55,7 +55,9 @@ CodeMirror.registerHelper("lint", "mgl", (text: string, opts: any, doc: any) =>
|
||||
try {
|
||||
parser.parse(text);
|
||||
}
|
||||
catch (e) {}
|
||||
catch (e) {
|
||||
// ignore errors
|
||||
}
|
||||
|
||||
if (found.length > 0) {
|
||||
// JSON invalid so don't go any further
|
||||
@@ -154,7 +156,7 @@ CodeMirror.registerHelper("lint", "mgl", (text: string, opts: any, doc: any) =>
|
||||
found.push(err);
|
||||
}
|
||||
else if (key) {
|
||||
const path = key.replace(/^\[|\]$/g, "").split(/\.|[\[\]]+/).filter(Boolean)
|
||||
const path = key.replace(/^\[|\]$/g, "").split(/\.|[[\]]+/).filter(Boolean)
|
||||
const parsedError = getArrayPositionalFromAst(ast, path);
|
||||
if (!parsedError) {
|
||||
console.warn("Something went wrong parsing error:", error);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* If we don't have a default value just make one up
|
||||
*/
|
||||
export function findDefaultFromSpec(spec: { type: 'string' | 'color' | 'boolean' | 'array', default?: any }) {
|
||||
if (spec.hasOwnProperty('default')) {
|
||||
if (Object.prototype.hasOwnProperty.call(spec, 'default')) {
|
||||
return spec.default;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"types": ["geojson"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user