Add lint to CI and fix errors (#853)

Adds lint to CI and fixes errors.
I'm not sure I'm fully proud of all the solutions there.
But there's no lint issues and the lint is being checked as part of the
CI.

---------

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