Merge branch 'main' into dependabot/npm_and_yarn/multi-422f831423

This commit is contained in:
Harel M
2025-03-03 16:44:07 +02:00
committed by GitHub
112 changed files with 2280 additions and 1989 deletions

View File

@@ -1,46 +0,0 @@
{
"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],
"no-var": ["error"]
},
"globals": {
"global": "readonly"
}
}

View File

@@ -8,4 +8,3 @@ assignees: ''
---
<!-- Thanks for reaching out! If you are having general Maputnik mapping questions, please asking them at https://gis.stackexchange.com/ using the 'maputnik' tag https://gis.stackexchange.com/questions/tagged/maputnik and read https://gis.stackexchange.com/help/how-to-ask before you do so (please keep in mind that you're asking there in a general GIS forum, not a dedicated support channel) -->

View File

@@ -8,4 +8,3 @@
- [ ] Include before/after visuals or gifs if this PR includes visual changes.
- [ ] Write tests for all new functionality.
- [ ] Add an entry to `CHANGELOG.md` under the `## main` section.

View File

@@ -11,7 +11,7 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2.2.0
uses: dependabot/fetch-metadata@v2.3.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve Dependabot PRs

View File

@@ -14,8 +14,8 @@ jobs:
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
steps:
- uses: actions/checkout@v4
- run: docker build -t test-docker-image-build .
- uses: actions/checkout@v4
- run: docker build -t test-docker-image-build .
# build the editor
build-node:
@@ -27,17 +27,17 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ ubuntu-latest, windows-latest, macos-latest ]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci
- run: npm run build
- run: npm run lint
- run: npm run lint-css
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci
- run: npm run build
- run: npm run lint
- run: npm run lint-css
@@ -47,53 +47,53 @@ jobs:
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci
- run: npm run build
- name: artifacts/maputnik
uses: actions/upload-artifact@v4
with:
name: maputnik
path: dist
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci
- run: npm run build
- name: artifacts/maputnik
uses: actions/upload-artifact@v4
with:
name: maputnik
path: dist
# Build and upload desktop CLI artifacts
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ^1.23.x
cache-dependency-path: desktop/go.sum
id: go
# Build and upload desktop CLI artifacts
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ^1.23.x
cache-dependency-path: desktop/go.sum
id: go
- name: Build desktop artifacts
run: npm run build-desktop
- name: Build desktop artifacts
run: npm run build-desktop
- name: Artifacts/linux
uses: actions/upload-artifact@v4
with:
name: maputnik-linux
path: ./desktop/bin/linux/
- name: Artifacts/linux
uses: actions/upload-artifact@v4
with:
name: maputnik-linux
path: ./desktop/bin/linux/
- name: Artifacts/darwin
uses: actions/upload-artifact@v4
with:
name: maputnik-darwin
path: ./desktop/bin/darwin/
- name: Artifacts/darwin
uses: actions/upload-artifact@v4
with:
name: maputnik-darwin
path: ./desktop/bin/darwin/
- name: Artifacts/windows
uses: actions/upload-artifact@v4
with:
name: maputnik-windows
path: ./desktop/bin/windows/
- name: Artifacts/windows
uses: actions/upload-artifact@v4
with:
name: maputnik-windows
path: ./desktop/bin/windows/
e2e-tests:
name: "E2E tests using ${{ matrix.browser }}"
strategy:
fail-fast: false
matrix:
browser: [chrome, firefox]
browser: [ chrome ]
runs-on: ubuntu-22.04
steps:

70
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '17 0 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

View File

@@ -20,7 +20,7 @@ jobs:
- name: Install
run: npm ci
- name: Build
- name: Build
run: npm run build
- name: Upload to GitHub Pages
@@ -28,7 +28,7 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: dist
# publish docker to GitHub registry
deploy-docker:
name: deploy/docker
@@ -40,12 +40,12 @@ jobs:
fail-fast: false
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4
- run: docker build -t ghcr.io/maplibre/maputnik:main .
- run: docker push ghcr.io/maplibre/maputnik:main
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4
- run: docker build -t ghcr.io/maplibre/maputnik:main .
- run: docker push ghcr.io/maplibre/maputnik:main

View File

@@ -2,7 +2,7 @@ name: Release
on:
push:
branches: [main]
branches: [ main ]
workflow_dispatch:
jobs:
@@ -99,4 +99,3 @@ jobs:
allowUpdates: true
draft: false
prerelease: false

24
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,24 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
ci:
autoupdate_schedule: monthly
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-executables-have-shebangs
- id: check-json
exclude: 'tsconfig(\.node)?\.json'
- id: check-shebang-scripts-are-executable
- id: check-symlinks
- id: check-toml
- id: check-yaml
args: [ --allow-multiple-documents ]
- id: destroyed-symlinks
- id: end-of-file-fixer
- id: mixed-line-ending
args: [ --fix=lf ]
- id: trailing-whitespace

View File

@@ -13,6 +13,8 @@
- _...Add new stuff here..._
### 🐞 Bug fixes
- Fix incorrect handing of network error response (#944)
- _...Add new stuff here..._
## 2.1.1
@@ -30,4 +32,3 @@
## 1.7.0
- See release notes at https://maputnik.github.io/blog/2020/04/23/release-v1.7.0

View File

@@ -20,4 +20,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -36,7 +36,7 @@ Maputnik is written in typescript and is using [React](https://github.com/facebo
We ensure building and developing Maputnik works with the [current active LTS Node.js version and above](https://github.com/nodejs/Release#release-schedule).
Check out our [Internationalization guide](./src/locales/README.md) for UI text related changes.
Check out our [Internationalization guide](./src/locales/README.md) for UI text related changes.
### Getting Involved
Join the #maplibre or #maputnik slack channel at OSMUS: get an invite at https://slack.openstreetmap.us/ Read the the below guide in order to get familiar with how we do things around here.

View File

@@ -1,6 +1,6 @@
# Build Scripts
This folder holds common build scripts used by some of the Github workflows.
This folder holds common build scripts used by some of the Github workflows.
The scripts are borrowed from [maplibre/maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js/tree/bc70bc559cea5c987fa1b79fd44766cef68bbe28/build).

View File

@@ -20,14 +20,14 @@ const changelog = fs.readFileSync(changelogPath, 'utf8');
*/
const regex = /^## (\d+\.\d+\.\d+.*?)\n(.+?)(?=\n^## \d+\.\d+\.\d+.*?\n)/gms;
let releaseNotes = [];
const releaseNotes = [];
let match;
// eslint-disable-next-line no-cond-assign
while (match = regex.exec(changelog)) {
releaseNotes.push({
'version': match[1],
'changelog': match[2].trim(),
});
releaseNotes.push({
'version': match[1],
'changelog': match[2].trim(),
});
}
const latest = releaseNotes[0];
@@ -44,5 +44,4 @@ const templatedReleaseNotes = `${header}
${latest.changelog}`;
// eslint-disable-next-line eol-last
process.stdout.write(templatedReleaseNotes.trimEnd());

View File

@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver";
describe("accessibility", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver();
const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter();
describe("skip links", () => {

View File

@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver";
describe("history", () => {
let { beforeAndAfter, when, get, then } = new MaputnikDriver();
const { beforeAndAfter, when, get, then } = new MaputnikDriver();
beforeAndAfter();
let undoKeyCombo: string;

View File

@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver";
describe("i18n", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver();
const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter();
describe("language detector", () => {

View File

@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver";
describe("keyboard", () => {
let { beforeAndAfter, given, when, get, then } = new MaputnikDriver();
const { beforeAndAfter, given, when, get, then } = new MaputnikDriver();
beforeAndAfter();
describe("shortcuts", () => {
beforeEach(() => {

View File

@@ -2,7 +2,7 @@ import { v1 as uuid } from "uuid";
import { MaputnikDriver } from "./maputnik-driver";
describe("layers", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver();
const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter();
beforeEach(() => {
when.setStyle("both");
@@ -101,7 +101,7 @@ describe("layers", () => {
});
describe("background", () => {
it("add", () => {
let id = when.modal.fillLayers({
const id = when.modal.fillLayers({
type: "background",
});
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
@@ -117,7 +117,7 @@ describe("layers", () => {
describe("modify", () => {
function createBackground() {
// Setup
let id = uuid();
const id = uuid();
when.selectWithin("add-layer.layer-type", "background");
when.setValue("add-layer.layer-id.input", "background:" + id);
@@ -139,11 +139,11 @@ describe("layers", () => {
describe("layer", () => {
it("expand/collapse");
it("id", () => {
let bgId = createBackground();
const bgId = createBackground();
when.click("layer-list-item:background:" + bgId);
let id = uuid();
const id = uuid();
when.setValue("layer-editor.layer-id.input", "foobar:" + id);
when.click("min-zoom");
@@ -219,7 +219,7 @@ describe("layers", () => {
describe("comments", () => {
let bgId: string;
let comment = "42";
const comment = "42";
beforeEach(() => {
bgId = createBackground();
@@ -320,11 +320,11 @@ describe("layers", () => {
// TODO
it.skip("parse error", () => {
let bgId = createBackground();
const bgId = createBackground();
when.click("layer-list-item:background:" + bgId);
let errorSelector = ".CodeMirror-lint-marker-error";
const errorSelector = ".CodeMirror-lint-marker-error";
then(get.elementByTestId(errorSelector)).shouldNotExist();
when.click(".CodeMirror");
@@ -339,7 +339,7 @@ describe("layers", () => {
describe("fill", () => {
it("add", () => {
let id = when.modal.fillLayers({
const id = when.modal.fillLayers({
type: "fill",
layer: "example",
});
@@ -361,7 +361,7 @@ describe("layers", () => {
describe("line", () => {
it("add", () => {
let id = when.modal.fillLayers({
const id = when.modal.fillLayers({
type: "line",
layer: "example",
});
@@ -385,7 +385,7 @@ describe("layers", () => {
describe("symbol", () => {
it("add", () => {
let id = when.modal.fillLayers({
const id = when.modal.fillLayers({
type: "symbol",
layer: "example",
});
@@ -404,7 +404,7 @@ describe("layers", () => {
describe("raster", () => {
it("add", () => {
let id = when.modal.fillLayers({
const id = when.modal.fillLayers({
type: "raster",
layer: "raster",
});
@@ -423,7 +423,7 @@ describe("layers", () => {
describe("circle", () => {
it("add", () => {
let id = when.modal.fillLayers({
const id = when.modal.fillLayers({
type: "circle",
layer: "example",
});
@@ -442,7 +442,7 @@ describe("layers", () => {
describe("fill extrusion", () => {
it("add", () => {
let id = when.modal.fillLayers({
const id = when.modal.fillLayers({
type: "fill-extrusion",
layer: "example",
});
@@ -494,4 +494,33 @@ describe("layers", () => {
).shouldBeVisible();
});
});
describe("layereditor jsonlint should error", ()=>{
it("add", () => {
const id = when.modal.fillLayers({
type: "circle",
layer: "example",
});
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{
id: id,
type: "circle",
source: "example",
},
],
});
const sourceText = get.elementByText('"source"');
sourceText.click();
sourceText.type("\"");
const error = get.element('.CodeMirror-lint-marker-error');
error.should('exist');
});
});
});

View File

@@ -1,11 +1,11 @@
import { MaputnikDriver } from "./maputnik-driver";
describe("map", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver();
const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter();
describe("zoom level", () => {
it("via url", () => {
let zoomLevel = 12.37;
const zoomLevel = 12.37;
when.setStyle("geojson", zoomLevel);
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible();
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText(
@@ -14,7 +14,7 @@ describe("map", () => {
});
it("via map controls", () => {
let zoomLevel = 12.37;
const zoomLevel = 12.37;
when.setStyle("geojson", zoomLevel);
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible();
when.clickZoomIn();
@@ -23,7 +23,7 @@ describe("map", () => {
);
});
});
describe("search", () => {
it('should exist', () => {
then(get.searchControl()).shouldBeVisible();

View File

@@ -8,8 +8,8 @@ export default class ModalDriver {
fillLayers: (opts: { type: string; layer?: string; id?: string }) => {
// Having logic in test code is an anti pattern.
// This should be splitted to multiple single responsibility functions
let type = opts.type;
let layer = opts.layer;
const type = opts.type;
const layer = opts.layer;
let id;
if (opts.id) {
id = opts.id;

View File

@@ -1,7 +1,8 @@
import { MaputnikDriver } from "./maputnik-driver";
import tokens from "../../src/config/tokens.json" with {type: "json"};
describe("modals", () => {
let { beforeAndAfter, when, get, then } = new MaputnikDriver();
const { beforeAndAfter, when, get, given, then } = new MaputnikDriver();
beforeAndAfter();
beforeEach(() => {
@@ -25,7 +26,7 @@ describe("modals", () => {
describe("when click open url", () => {
beforeEach(() => {
let styleFileUrl = get.exampleFileUrl();
const styleFileUrl = get.exampleFileUrl();
when.setValue("modal:open.url.input", styleFileUrl);
when.click("modal:open.url.button");
@@ -70,7 +71,7 @@ describe("modals", () => {
it("public source");
it("add new source", () => {
let sourceId = "n1z2v3r";
const sourceId = "n1z2v3r";
when.setValue("modal:sources.add.source_id", sourceId);
when.select("modal:sources.add.source_type", "tile_vector");
when.select("modal:sources.add.scheme_type", "tms");
@@ -83,8 +84,26 @@ describe("modals", () => {
});
});
it("add new pmtiles source", () => {
const sourceId = "pmtilestest";
when.setValue("modal:sources.add.source_id", sourceId);
when.select("modal:sources.add.source_type", "pmtiles_vector");
when.setValue("modal:sources.add.source_url", "https://data.source.coop/protomaps/openstreetmap/v4.pmtiles");
when.click("modal:sources.add.add_source");
when.click("modal:sources.add.add_source");
when.wait(200);
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
pmtilestest: {
type: "vector",
url: "pmtiles://https://data.source.coop/protomaps/openstreetmap/v4.pmtiles",
},
},
});
});
it("add new raster source", () => {
let sourceId = "rastertest";
const sourceId = "rastertest";
when.setValue("modal:sources.add.source_id", sourceId);
when.select("modal:sources.add.source_type", "tile_raster");
when.select("modal:sources.add.scheme_type", "xyz");
@@ -159,7 +178,7 @@ describe("modals", () => {
});
});
it("glyphs url", () => {
let glyphsUrl = "http://example.com/{fontstack}/{range}.pbf";
const glyphsUrl = "http://example.com/{fontstack}/{range}.pbf";
when.setValue("modal:settings.glyphs", glyphsUrl);
when.click("modal:settings.name");
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
@@ -168,7 +187,7 @@ describe("modals", () => {
});
it("maptiler access token", () => {
let apiKey = "testing123";
const apiKey = "testing123";
when.setValue(
"modal:settings.maputnik:openmaptiles_access_token",
apiKey
@@ -182,7 +201,7 @@ describe("modals", () => {
});
it("thunderforest access token", () => {
let apiKey = "testing123";
const apiKey = "testing123";
when.setValue(
"modal:settings.maputnik:thunderforest_access_token",
apiKey
@@ -194,7 +213,7 @@ describe("modals", () => {
});
it("stadia access token", () => {
let apiKey = "testing123";
const apiKey = "testing123";
when.setValue(
"modal:settings.maputnik:stadia_access_token",
apiKey
@@ -217,6 +236,40 @@ describe("modals", () => {
metadata: { "maputnik:renderer": "ol" },
});
});
it("inlcude API key when change renderer", () => {
when.click("modal:settings.close-modal")
when.click("nav:open");
get.elementByAttribute('aria-label', "MapTiler Basic").should('exist').click();
when.wait(1000);
when.click("nav:settings");
when.select("modal:settings.maputnik:renderer", "mlgljs");
then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual(
"mlgljs"
);
when.select("modal:settings.maputnik:renderer", "ol");
then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual(
"ol"
);
given.intercept("https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key=*", "tileRequest", "GET");
when.select("modal:settings.maputnik:renderer", "mlgljs");
then(get.inputValue("modal:settings.maputnik:renderer")).shouldEqual(
"mlgljs"
);
when.waitForResponse("tileRequest").its("request").its("url").should("include", `https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key=${tokens.openmaptiles}`);
when.waitForResponse("tileRequest").its("request").its("url").should("include", `https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key=${tokens.openmaptiles}`);
when.waitForResponse("tileRequest").its("request").its("url").should("include", `https://api.maptiler.com/tiles/v3-openmaptiles/tiles.json?key=${tokens.openmaptiles}`);
});
});
describe("sources", () => {

View File

@@ -15,4 +15,3 @@
}
]
}

View File

@@ -31,4 +31,4 @@
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
"layers": []
}
}

View File

@@ -26,4 +26,4 @@
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
"layers": []
}
}

View File

@@ -15,4 +15,4 @@
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
"layers": []
}
}

View File

@@ -34,4 +34,4 @@
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
// }

61
eslint.config.js Normal file
View File

@@ -0,0 +1,61 @@
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import reactRefreshPlugin from 'eslint-plugin-react-refresh';
export default tseslint.config({
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
],
files: ['**/*.{js,jsx,ts,tsx}'],
ignores: [
"dist/**/*",
],
languageOptions: {
ecmaVersion: 2024,
sourceType: 'module',
globals: {
global: 'readonly'
}
},
settings: {
react: { version: '18.2' }
},
plugins: {
'react': reactPlugin,
'react-hooks': reactHooksPlugin,
'react-refresh': reactRefreshPlugin
},
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true }
],
"@typescript-eslint/no-explicit-any": "off",
'@typescript-eslint/no-unused-vars': [
'warn',
{
varsIgnorePattern: '^_',
caughtErrors: 'all',
caughtErrorsIgnorePattern: '^_',
argsIgnorePattern: '^_'
}
],
'no-unused-vars': 'off',
'react/prop-types': 'off',
'no-undef': 'off',
'indent': ['error', 2],
'no-var': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
},
linterOptions: {
reportUnusedDisableDirectives: true,
noInlineConfig: false
}
}
)

View File

@@ -1,6 +1,6 @@
export default {
output: 'src/locales/$LOCALE/$NAMESPACE.json',
locales: [ 'de', 'fr', 'he', 'ja', 'zh' ],
locales: [ 'de', 'fr', 'he', 'it','ja', 'zh' ],
// Because some keys are dynamically generated, i18next-parser can't detect them.
// We add these keys manually, so we don't want to remove them.
@@ -10,7 +10,7 @@ export default {
keySeparator: false,
namespaceSeparator: false,
defaultValue: (locale, ns, key) => {
defaultValue: (_locale, _ns, _key) => {
// The default value is a string that indicates that the string is not translated.
return '__STRING_NOT_TRANSLATED__';
}

2534
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"build": "tsc && vite build --base=/maputnik/",
"build-desktop": "tsc && vite build --base=/ && cd desktop && make",
"i18n:refresh": "i18next 'src/**/*.{ts,tsx,js,jsx}'",
"lint": "eslint ./src ./cypress --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0",
"lint": "eslint",
"test": "cypress run",
"cy:open": "cypress open",
"lint-css": "stylelint \"src/styles/*.scss\"",
@@ -29,20 +29,20 @@
"@maplibre/maplibre-gl-style-spec": "^23.1.0",
"@mdi/js": "^7.4.47",
"@mdi/react": "^1.6.1",
"@prantlf/jsonlint": "^16.0.0",
"array-move": "^4.0.0",
"buffer": "^6.0.3",
"classnames": "^2.5.1",
"codemirror": "^5.65.2",
"color": "^4.2.3",
"codemirror": "^5.65.18",
"color": "^5.0.0",
"detect-browser": "^5.3.0",
"events": "^3.3.0",
"file-saver": "^2.0.5",
"i18next": "^24.2.1",
"i18next-browser-languagedetector": "^8.0.2",
"i18next": "^24.2.2",
"i18next-browser-languagedetector": "^8.0.4",
"i18next-resources-to-backend": "^1.2.1",
"json-stringify-pretty-compact": "^4.0.0",
"json-to-ast": "^2.1.0",
"jsonlint": "github:josdejong/jsonlint#85a19d7",
"lodash": "^4.17.21",
"lodash.capitalize": "^4.2.1",
"lodash.clamp": "^4.0.3",
@@ -50,10 +50,11 @@
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"maplibre-gl": "^5.0.1",
"maplibre-gl": "^5.1.1",
"maputnik-design": "github:maputnik/design#172b06c",
"ol": "^10.3.1",
"ol-mapbox-style": "^12.4.0",
"ol": "^10.4.0",
"ol-mapbox-style": "^12.5.0",
"pmtiles": "^4.3.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-accessible-accordion": "^5.0.0",
@@ -92,15 +93,16 @@
}
},
"devDependencies": {
"@cypress/code-coverage": "^3.13.10",
"@cypress/code-coverage": "^3.13.12",
"@eslint/js": "^9.19.0",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@rollup/plugin-replace": "^6.0.2",
"@shellygo/cypress-test-utils": "^4.1.11",
"@shellygo/cypress-test-utils": "^4.1.12",
"@types/codemirror": "^5.60.15",
"@types/color": "^4.2.0",
"@types/cors": "^2.8.17",
"@types/file-saver": "^2.0.7",
"@types/geojson": "^7946.0.14",
"@types/geojson": "^7946.0.16",
"@types/json-to-ast": "^2.1.4",
"@types/lodash.capitalize": "^4.2.9",
"@types/lodash.clamp": "^4.0.9",
@@ -108,7 +110,6 @@
"@types/lodash.get": "^4.4.9",
"@types/lodash.isequal": "^4.5.8",
"@types/lodash.throttle": "^4.1.9",
"@types/mocha": "^10.0.10",
"@types/randomcolor": "^0.5.9",
"@types/react": "^18.2.67",
"@types/react-aria-menubutton": "^6.2.14",
@@ -122,29 +123,27 @@
"@types/string-hash": "^1.1.3",
"@types/uuid": "^10.0.0",
"@types/wicg-file-system-access": "^2023.10.5",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"@vitejs/plugin-react": "^4.3.4",
"cors": "^2.8.5",
"cypress": "^14.0.0",
"cypress": "^14.1.0",
"cypress-plugin-tab": "^1.0.5",
"eslint": "^8.57.0",
"eslint": "^9.21.0",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.18",
"i18next-parser": "^9.1.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"i18next-parser": "^9.3.0",
"istanbul": "^0.4.5",
"istanbul-lib-coverage": "^3.2.2",
"mocha": "^11.0.1",
"postcss": "^8.4.38",
"postcss": "^8.5.1",
"react-hot-loader": "^4.13.1",
"sass": "^1.72.0",
"stylelint": "^16.13.2",
"sass": "^1.85.1",
"stylelint": "^16.15.0",
"stylelint-config-recommended-scss": "^14.1.0",
"stylelint-scss": "^6.10.1",
"typescript": "^5.4.3",
"uuid": "^11.0.5",
"vite": "^6.0.11",
"vite-plugin-istanbul": "^6.0.2"
"stylelint-scss": "^6.11.1",
"typescript": "^5.8.2",
"typescript-eslint": "^8.25.0",
"uuid": "^11.1.0",
"vite": "^6.2.0",
"vite-plugin-istanbul": "^7.0.0"
}
}

View File

@@ -8,6 +8,7 @@ import get from 'lodash.get'
import {unset} from 'lodash'
import {arrayMoveMutable} from 'array-move'
import hash from "string-hash";
import { PMTiles } from "pmtiles";
import {Map, LayerSpecification, StyleSpecification, ValidationError, SourceSpecification} from 'maplibre-gl'
import {latest, validateStyleMin} from '@maplibre/maplibre-gl-style-spec'
@@ -362,6 +363,7 @@ export default class App extends React.Component<any, AppState> {
[property]: value
}
}
this.onStyleChanged(changedStyle)
}
@@ -373,6 +375,24 @@ export default class App extends React.Component<any, AppState> {
...opts,
};
// For the style object, find the urls that has "{key}" and insert the correct API keys
// Without this, going from e.g. MapTiler to OpenLayers and back will lose the maptlier key.
if (newStyle.glyphs && typeof newStyle.glyphs === 'string') {
newStyle.glyphs = setFetchAccessToken(newStyle.glyphs, newStyle);
}
if (newStyle.sprite && typeof newStyle.sprite === 'string') {
newStyle.sprite = setFetchAccessToken(newStyle.sprite, newStyle);
}
for (const [_sourceId, source] of Object.entries(newStyle.sources)) {
if (source && 'url' in source && typeof source.url === 'string') {
source.url = setFetchAccessToken(source.url, newStyle);
}
}
if (opts.initialLoad) {
this.getInitialStateFromUrl(newStyle);
}
@@ -641,33 +661,41 @@ export default class App extends React.Component<any, AppState> {
console.warn("Failed to setFetchAccessToken: ", err);
}
fetch(url!, {
mode: 'cors',
})
.then(response => response.json())
.then(json => {
const setVectorLayers = (json:any) => {
if(!Object.prototype.hasOwnProperty.call(json, "vector_layers")) {
return;
}
if(!Object.prototype.hasOwnProperty.call(json, "vector_layers")) {
return;
}
// Create new objects before setState
const sources = Object.assign({}, {
[key]: this.state.sources[key],
});
for(const layer of json.vector_layers) {
(sources[key] as any).layers.push(layer.id)
}
console.debug("Updating source: "+key);
this.setState({
sources: sources
});
})
.catch(err => {
console.error("Failed to process sources for '%s'", url, err);
// Create new objects before setState
const sources = Object.assign({}, {
[key]: this.state.sources[key],
});
for(const layer of json.vector_layers) {
(sources[key] as any).layers.push(layer.id)
}
this.setState({
sources: sources
});
};
if (url!.startsWith("pmtiles://")) {
(new PMTiles(url!.substr(10))).getTileJson("")
.then(json => setVectorLayers(json))
.catch(err => {
console.error("Failed to process sources for '%s'", url, err);
});
} else {
fetch(url!, {
mode: 'cors',
})
.then(response => response.json())
.then(json => setVectorLayers(json))
.catch(err => {
console.error("Failed to process sources for '%s'", url, err);
});
}
}
else {
sourceList[key] = this.state.sources[key] || this.state.mapStyle.sources[key];
@@ -728,6 +756,7 @@ export default class App extends React.Component<any, AppState> {
onLayerSelect={this.onLayerSelect}
/>
} else {
mapElement = <MapMaplibreGl {...mapProps}
onChange={this.onMapChange}
options={this.state.maplibreGlDebugOptions}
@@ -781,6 +810,7 @@ export default class App extends React.Component<any, AppState> {
getInitialStateFromUrl = (mapStyle: StyleSpecification) => {
const url = new URL(location.href);
const modalParam = url.searchParams.get("modal");
if (modalParam && modalParam !== "") {
const modals = modalParam.split(",");
const modalObj: {[key: string]: boolean} = {};

View File

@@ -102,4 +102,3 @@ export default class Block extends React.Component<BlockProps, BlockState> {
</label>
}
}

View File

@@ -31,4 +31,3 @@ export default class Collapse extends React.Component<CollapseProps> {
}
}
}

View File

@@ -16,4 +16,3 @@ export default class Collapser extends React.Component<CollapserProps> {
return this.props.isCollapsed ? <MdArrowDropUp style={iconStyle}/> : <MdArrowDropDown style={iconStyle} />
}
}

View File

@@ -16,4 +16,3 @@ export default class FieldArray extends React.Component<FieldArrayProps> {
</Fieldset>
}
}

View File

@@ -15,4 +15,3 @@ export default class FieldAutocomplete extends React.Component<FieldAutocomplete
</Block>
}
}

View File

@@ -15,4 +15,3 @@ export default class FieldCheckbox extends React.Component<FieldCheckboxProps> {
</Block>
}
}

View File

@@ -18,4 +18,3 @@ export default class FieldColor extends React.Component<FieldColorProps> {
</Block>
}
}

View File

@@ -13,4 +13,3 @@ export default class FieldDynamicArray extends React.Component<FieldDynamicArray
</Fieldset>
}
}

View File

@@ -3,7 +3,7 @@ import InputEnum, {InputEnumProps} from './InputEnum'
import Fieldset from './Fieldset';
type FieldEnumProps = InputEnumProps & {
type FieldEnumProps = InputEnumProps & {
label?: string;
fieldSpec?: {
doc: string
@@ -17,4 +17,4 @@ export default class FieldEnum extends React.Component<FieldEnumProps> {
<InputEnum {...this.props} />
</Fieldset>
}
}
}

View File

@@ -404,4 +404,3 @@ export default class FieldFunction extends React.Component<FieldFunctionProps, F
</div>
}
}

View File

@@ -10,4 +10,3 @@ export default class FieldJson extends React.Component<FieldJsonProps> {
return <InputJson {...this.props} />
}
}

View File

@@ -15,4 +15,3 @@ export default class FieldMultiInput extends React.Component<FieldMultiInputProp
</Fieldset>
}
}

View File

@@ -18,5 +18,3 @@ export default class FieldSelect extends React.Component<FieldSelectProps> {
</Block>
}
}

View File

@@ -22,8 +22,8 @@ class FieldSourceLayerInternal extends React.Component<FieldSourceLayerInternalP
render() {
const t = this.props.t;
return <Block
label={t("Source Layer")}
return <Block
label={t("Source Layer")}
fieldSpec={latest.layer['source-layer']}
data-wd-key="layer-source-layer"
error={this.props.error}

View File

@@ -20,4 +20,3 @@ export default class FieldUrl extends React.Component<FieldUrlProps> {
);
}
}

View File

@@ -255,8 +255,8 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
value={combiningOp}
onChange={(v: [string, any]) => this.onFilterPartChanged(0, v)}
options={[
["all", t("every filter matches")],
["none", t("no filter matches")],
["all", t("every filter matches")],
["none", t("no filter matches")],
["any", t("any filter matches")]
]}
/>

View File

@@ -11,5 +11,3 @@ export default class IconBackground extends React.Component {
)
}
}

View File

@@ -11,5 +11,3 @@ export default class IconCircle extends React.Component {
)
}
}

View File

@@ -11,5 +11,3 @@ export default class IconFill extends React.Component {
)
}
}

View File

@@ -30,4 +30,3 @@ export default class IconLayer extends React.Component<IconLayerProps> {
}
}
}

View File

@@ -11,5 +11,3 @@ export default class IconLine extends React.Component {
)
}
}

View File

@@ -13,6 +13,3 @@ export default class IconSymbol extends React.Component {
)
}
}

View File

@@ -115,4 +115,3 @@ export default class FieldArray extends React.Component<FieldArrayProps, FieldAr
)
}
}

View File

@@ -98,5 +98,3 @@ export default class InputAutocomplete extends React.Component<InputAutocomplete
</div>
}
}

View File

@@ -30,4 +30,4 @@ export default class InputButton extends React.Component<InputButtonProps> {
{this.props.children}
</button>
}
}
}

View File

@@ -34,4 +34,4 @@ export default class InputCheckbox extends React.Component<InputCheckboxProps> {
</div>
</div>
}
}
}

View File

@@ -133,4 +133,3 @@ export default class InputColor extends React.Component<InputColorProps> {
</div>
}
}

View File

@@ -22,7 +22,7 @@ export type FieldDynamicArrayProps = {
}
'aria-label'?: string
label: string
}
}
type FieldDynamicArrayInternalProps = FieldDynamicArrayProps & WithTranslation;
@@ -67,7 +67,7 @@ class FieldDynamicArrayInternal extends React.Component<FieldDynamicArrayInterna
const t = this.props.t;
const i18nProps = { t, i18n: this.props.i18n, tReady: this.props.tReady };
const inputs = this.values.map((v, i) => {
const deleteValueBtn= <DeleteValueInputButton
const deleteValueBtn= <DeleteValueInputButton
onClick={this.deleteValue.bind(this, i)}
{...i18nProps}
/>;
@@ -152,4 +152,3 @@ class DeleteValueInputButton extends React.Component<DeleteValueInputButtonProps
</InputButton>
}
}

View File

@@ -47,4 +47,3 @@ export default class InputEnum extends React.Component<InputEnumProps> {
}
}
}

View File

@@ -37,5 +37,3 @@ export default class InputMultiInput extends React.Component<InputMultiInputProp
</fieldset>
}
}

View File

@@ -157,7 +157,7 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
render() {
if(
Object.prototype.hasOwnProperty.call(this.props, "min") &&
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
@@ -244,5 +244,3 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
}
}
}

View File

@@ -30,5 +30,3 @@ export default class InputSelect extends React.Component<InputSelectProps> {
</select>
}
}

View File

@@ -97,5 +97,3 @@ export default class InputString extends React.Component<InputStringProps, Input
});
}
}

View File

@@ -15,7 +15,7 @@ function validate(url: string, t: TFunction): JSX.Element | undefined {
const urlObj = new URL(url);
return urlObj.protocol;
}
catch (err) {
catch (_err) {
return undefined;
}
};

View File

@@ -326,10 +326,10 @@ class LayerEditorInternal extends React.Component<LayerEditorInternalProps, Laye
onSelection={handleSelection}
closeOnSelection={false}
>
<Button
id="skip-target-layer-editor"
data-wd-key="skip-target-layer-editor"
className='more-menu__button'
<Button
id="skip-target-layer-editor"
data-wd-key="skip-target-layer-editor"
className='more-menu__button'
title={"Layer options"}>
<MdMoreVert className="more-menu__button__svg" />
</Button>

View File

@@ -287,9 +287,9 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
data-wd-key="skip-target-layer-list"
onClick={this.toggleLayers}
className="maputnik-button">
{this.state.areAllGroupsExpanded === true ?
t("Collapse")
:
{this.state.areAllGroupsExpanded === true ?
t("Collapse")
:
t("Expand")
}
</button>

View File

@@ -15,6 +15,7 @@ import MaplibreGeocoder, { MaplibreGeocoderApi, MaplibreGeocoderApiConfig } from
import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css';
import { withTranslation, WithTranslation } from 'react-i18next'
import i18next from 'i18next'
import { Protocol } from "pmtiles";
function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container): HTMLElement {
ReactDOM.render(popup, mountNode);
@@ -103,7 +104,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
let should = false;
try {
should = JSON.stringify(this.props) !== JSON.stringify(nextProps) || JSON.stringify(this.state) !== JSON.stringify(nextState);
} catch(e) {
} catch(_e) {
// no biggie, carry on
}
return should;
@@ -133,7 +134,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
this.state.inspect!.render();
}, 500);
}
}
componentDidMount() {
@@ -148,6 +149,8 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
localIdeographFontFamily: false
} satisfies MapOptions;
const protocol = new Protocol({metadata: true});
MapLibreGl.addProtocol("pmtiles",protocol.tile);
const map = new MapLibreGl.Map(mapOpts);
const mapViewChange = () => {
@@ -161,7 +164,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
map.showCollisionBoxes = mapOpts.showCollisionBoxes!;
map.showOverdrawInspector = mapOpts.showOverdrawInspector!;
let geocoder = this.initGeocoder(map);
const geocoder = this.initGeocoder(map);
const zoomControl = new ZoomControl();
map.addControl(zoomControl, 'top-right');

View File

@@ -27,7 +27,7 @@ class ModalLoadingInternal extends React.Component<ModalLoadingInternalProps> {
underlayClickExits={false}
underlayProps={{
// @ts-ignore
onClick: (e: Event) => underlayProps(e)
onClick: (e: Event) => underlayProps(e)
}}
title={this.props.title}
onOpenToggle={() => this.props.onCancel()}

View File

@@ -51,6 +51,7 @@ function editorMode(source: SourceSpecification) {
}
if(source.type === 'vector') {
if(source.tiles) return 'tile_vector'
if(source.url && source.url.startsWith("pmtiles://")) return 'pmtiles_vector'
return 'tilejson_vector'
}
if(source.type === 'geojson') {
@@ -129,6 +130,10 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
const {protocol} = window.location;
switch(mode) {
case 'pmtiles_vector': return {
type: 'vector',
url: `${protocol}//localhost:3000/file.pmtiles`
}
case 'geojson_url': return {
type: 'geojson',
data: `${protocol}//localhost:3000/geojson.json`
@@ -240,6 +245,7 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
['tile_raster', t('Raster (Tile URLs)')],
['tilejson_raster-dem', t('Raster DEM (TileJSON URL)')],
['tilexyz_raster-dem', t('Raster DEM (XYZ URLs)')],
['pmtiles_vector', t('Vector (PMTiles)')],
['image', t('Image')],
['video', t('Video')],
]}

View File

@@ -11,7 +11,7 @@ import FieldCheckbox from './FieldCheckbox'
import { WithTranslation, withTranslation } from 'react-i18next';
import { TFunction } from 'i18next'
export type EditorMode = "video" | "image" | "tilejson_vector" | "tile_raster" | "tilejson_raster" | "tilexyz_raster-dem" | "tilejson_raster-dem" | "tile_vector" | "geojson_url" | "geojson_json" | null;
export type EditorMode = "video" | "image" | "tilejson_vector" | "tile_raster" | "tilejson_raster" | "tilexyz_raster-dem" | "tilejson_raster-dem" | "pmtiles_vector" | "tile_vector" | "geojson_url" | "geojson_json" | null;
type TileJSONSourceEditorProps = {
source: {
@@ -286,6 +286,33 @@ class GeoJSONSourceFieldJsonEditor extends React.Component<GeoJSONSourceFieldJso
}
}
type PMTilesSourceEditorProps = {
source: {
url: string
}
onChange(...args: unknown[]): unknown
children?: React.ReactNode
} & WithTranslation;
class PMTilesSourceEditor extends React.Component<PMTilesSourceEditorProps> {
render() {
const t = this.props.t;
return <div>
<FieldUrl
label={t("PMTiles URL")}
fieldSpec={latest.source_vector.url}
value={this.props.source.url}
data-wd-key="modal:sources.add.source_url"
onChange={(url: string) => this.props.onChange({
...this.props.source,
url: url.startsWith("pmtiles://") ? url : `pmtiles://${url}`
})}
/>
{this.props.children}
</div>
}
}
type ModalSourcesTypeEditorInternalProps = {
mode: EditorMode
source: any
@@ -343,6 +370,7 @@ class ModalSourcesTypeEditorInternal extends React.Component<ModalSourcesTypeEdi
value={this.props.source.encoding || latest.source_raster_dem.encoding.default}
/>
</TileURLSourceEditor>
case 'pmtiles_vector': return <PMTilesSourceEditor {...commonProps} />
case 'image': return <ImageSourceEditor {...commonProps} />
case 'video': return <VideoSourceEditor {...commonProps} />
default: return null

View File

@@ -56,8 +56,8 @@ export default class PropertyGroup extends React.Component<PropertyGroupProps> {
const paint = this.props.layer.paint || {}
const layout = this.props.layer.layout || {}
const fieldValue = fieldName in paint
? paint[fieldName as keyof typeof paint]
const fieldValue = fieldName in paint
? paint[fieldName as keyof typeof paint]
: layout[fieldName as keyof typeof layout]
const fieldType = fieldName in paint ? 'paint' : 'layout';

View File

@@ -11,4 +11,3 @@ export default class ScrollContainer extends React.Component<ScrollContainerProp
</div>
}
}

View File

@@ -90,4 +90,3 @@ export default class SingleFilterEditor extends React.Component<SingleFilterEdit
</div>
}
}

View File

@@ -1,7 +1,7 @@
@import '../styles/vars';
@use '../styles/vars';
.SmallError {
color: #E57373;
font-size: $font-size-6;
margin-top: $margin-2
font-size: vars.$font-size-6;
margin-top: vars.$margin-2
}

View File

@@ -44,4 +44,3 @@ export default class SpecField extends React.Component<SpecFieldProps> {
</TypeBlock>
}
}

View File

@@ -91,7 +91,7 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
function getValue(data: any) {
return stringifyPretty(data, {indent: 2, maxLength: 38})
}
if (jsonError) {
foundErrors.push({message: "Invalid JSON"});
}

View File

@@ -29,8 +29,8 @@ export default class SpecProperty extends React.Component<SpecPropertyProps> {
const functionBtn = <FunctionButtons
fieldSpec={this.props.fieldSpec}
onZoomClick={this.props.onZoomClick}
onDataClick={this.props.onDataClick}
onExpressionClick={this.props.onExpressionClick}
onDataClick={this.props.onDataClick}
onExpressionClick={this.props.onExpressionClick}
/>
const error = errors![fieldType+"."+fieldName as any] as any;

View File

@@ -8,6 +8,7 @@ export const supportedLanguages = {
"en": "English",
"fr": "Français",
"he": "עברית",
"it": "Italiano",
"ja": "日本語",
"zh": "简体中文"
} as const;

View File

@@ -1,5 +1,4 @@
// @ts-ignore - this is a fork of jsonlint
import jsonlint from 'jsonlint';
import {parse} from '@prantlf/jsonlint';
import CodeMirror, { MarkerRange } from 'codemirror';
import jsonToAst from 'json-to-ast';
import {expression, validateStyleMin} from '@maplibre/maplibre-gl-style-spec';
@@ -15,49 +14,45 @@ CodeMirror.defineMode("mgl", (config, parserConfig) => {
);
});
CodeMirror.registerHelper("lint", "json", (text: string) => {
function tryToParse(text: string) {
const found: MarkerRangeWithMessage[] = [];
// NOTE: This was modified from the original to remove the global, also the
// old jsonlint API was 'jsonlint.parseError' its now
// 'jsonlint.parser.parseError'
(jsonlint as any).parser.parseError = (str: string, hash: any) => {
const loc = hash.loc;
found.push({
from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str
});
};
try {
jsonlint.parse(text);
parse(text);
}
catch(e) {
// Do nothing we catch the error above
catch(err: any) {
const errorMatch = err.toString().match(/line (\d+), column (\d+)/);
if (errorMatch) {
const loc = {
first_line: parseInt(errorMatch[1], 10),
first_column: parseInt(errorMatch[2], 10),
last_line: parseInt(errorMatch[1], 10),
last_column: parseInt(errorMatch[2], 10)
};
// const loc = hash.loc;
found.push({
from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: err
});
}
}
return found;
}
CodeMirror.registerHelper("lint", "json", (text: string) => {
return tryToParse(text);
});
CodeMirror.registerHelper("lint", "mgl", (text: string, opts: any, doc: any) => {
const found: MarkerRangeWithMessage[] = [];
const {parser} = jsonlint as any;
const {context} = opts;
parser.parseError = (str: string, hash: any) => {
const loc = hash.loc;
found.push({
from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str
});
};
try {
parser.parse(text);
}
catch (e) {
// ignore errors
}
const found: MarkerRangeWithMessage[] = tryToParse(text);
const {context} = opts;
if (found.length > 0) {
// JSON invalid so don't go any further

View File

@@ -11,4 +11,4 @@ export function undoMessages(beforeStyle: StyleSpecification, afterStyle: StyleS
}
export function redoMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) {
return diffMessages(beforeStyle, afterStyle).map(m => 'Redo ' + m)
}
}

View File

@@ -5,14 +5,17 @@ function loadJSON(url: string, defaultValue: any, cb: (...args: any[]) => void)
mode: 'cors',
credentials: "same-origin"
})
.then(function(response) {
.then((response) => {
if (!response.ok) {
throw new Error('Failed to load metadata for ' + url);
}
return response.json();
})
.then(function(body) {
.then((body) => {
cb(body)
})
.catch(function() {
console.warn('Can not metadata for ' + url)
.catch(() => {
console.warn('Can not load metadata for ' + url + ', using default value ' + defaultValue);
cb(defaultValue)
})
}

View File

@@ -24,4 +24,3 @@ export function changeSource(mapStyle: StyleSpecification, sourceId: string, sou
sources: changedSources
}
}

View File

@@ -1,4 +1,3 @@
// @ts-ignore
import style from './style'
export function initialStyleUrl() {

View File

@@ -14,10 +14,10 @@ export default class ZoomControl {
this._container.setAttribute("data-wd-key", "maplibre:ctrl-zoom");
this.setLabel("Zoom:");
this.addEventListeners();
return this._container;
}
updateZoomLevel() {
this._textEl!.innerHTML = this._map!.getZoom().toFixed(2);
}
@@ -29,7 +29,7 @@ export default class ZoomControl {
this._textEl = this._container!.querySelector("span");
this.updateZoomLevel();
}
addEventListeners (){
this._map!.on('render', () => this.updateZoomLevel());
this._map!.on('zoomIn', () => this.updateZoomLevel());

View File

@@ -46,15 +46,14 @@ The following users can help you with the relevant languages:
| ISO Code | Language | User |
|----------|--------------------|--------------------------------------------|
| de | German | [@josxha](https://github.com/josxha) |
| en | English | [@HarelM](https://github.com/HarelM) |
| fr | French | [@lhapaipai](https://github.com/lhapaipai) |
| hr | Hebrew | [@HarelM](https://github.com/HarelM) |
| ja | Japanese | [@keichan34](https://github.com/keichan34) |
| zh | Simplified Chinese | [@jieme](https://github.com/jieme) |
| de | German | [@josxha](https://github.com/josxha) |
| en | English | [@HarelM](https://github.com/HarelM) |
| fr | French | [@lhapaipai](https://github.com/lhapaipai) |
| hr | Hebrew | [@HarelM](https://github.com/HarelM) |
| it | Italian | [@napo](https://github.com/napo) |
| ja | Japanese | [@keichan34](https://github.com/keichan34) |
| zh | Simplified Chinese | [@jieme](https://github.com/jieme) |
You can test the UI in different languages using the dropdown in the top menu
Note that Maputnik automatically localize based on browser language settings and stores this language in local storage.
You can use incognito mode to check a first time usage.

View File

@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "Raster (Tile URLs)",
"Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)",
"Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)",
"Vector (PMTiles)": "Vektor (PMTiles)",
"Image": "Bild",
"Video": "Video",
"Add Source": "Quelle hinzufügen",
@@ -170,6 +171,7 @@
"GeoJSON URL": "GeoJSON URL",
"GeoJSON": "GeoJSON",
"Cluster": "Cluster",
"PMTiles URL": "PMTiles URL",
"Tile Size": "Kachelgröße",
"Encoding": "Kodierung",
"Error:": "Fehler:",

View File

@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "Raster (URLs Tile)",
"Raster DEM (TileJSON URL)": "Raster DEM (URL TileJSON)",
"Raster DEM (XYZ URLs)": "Raster DEM (URLs XYZ)",
"Vector (PMTiles)": "Vecteur (PMTiles)",
"Image": "Image",
"Video": "Vidéo",
"Add Source": "Ajouter une source",
@@ -170,6 +171,7 @@
"GeoJSON URL": "URL GeoJSON",
"GeoJSON": "GeoJSON",
"Cluster": "Cluster",
"PMTiles URL": "URL PMTiles",
"Tile Size": "Dimension d'une tuile",
"Encoding": "Encodage",
"Error:": "Erreur :",

View File

@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "Raster (Tile URLs)",
"Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)",
"Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)",
"Vector (PMTiles)": "Vector (PMTiles)",
"Image": "תמונה",
"Video": "וידאו",
"Add Source": "הוספת מקור",
@@ -170,6 +171,7 @@
"GeoJSON URL": "כתובת GeoJSON",
"GeoJSON": "GeoJSON",
"Cluster": "קיבוץ",
"PMTiles URL": "כתובת PMTiles",
"Tile Size": "גודל אריח",
"Encoding": "קידוד",
"Error:": "שגיאה",

View File

@@ -0,0 +1,189 @@
{
"Input value": "Valore di input",
"Data value": "Valore dei dati",
"Output value": "Valore di output",
"Function": "Funzione",
"Select a type of data scale (default is 'categorical').": "Seleziona un tipo di scala dati (predefinito 'categorical').",
"Base": "Base",
"Input a data property to base styles off of.": "Inserisci una proprietà dati su cui basare gli stili.",
"Default": "Predefinito",
"Stops": "Fermate",
"Zoom": "Zoom",
"Add stop": "Aggiungi fermata",
"Convert to expression": "Converti in espressione",
"Remove zoom level from stop": "Rimuovi il livello di zoom dalla fermata",
"Revert from expression": "Ripristina dall'espressione",
"Delete expression": "Elimina espressione",
"Convert property into a zoom function": "Converti proprietà in una funzione di zoom",
"Convert property to data function": "Converti proprietà in una funzione dati",
"Layer <1>{formatLayerId(layerId)}</1>: {parsed.data.message}": "Livello <1>{formatLayerId(layerId)}</1>: {parsed.data.message}",
"switch to layer": "passa al livello",
"Map": "Mappa",
"Inspect": "Esamina",
"Deuteranopia filter": "Filtro deuteranopia",
"Protanopia filter": "Filtro protanopia",
"Tritanopia filter": "Filtro tritanopia",
"Achromatopsia filter": "Filtro acromatopsia",
"Layers list": "Elenco livelli",
"Layer editor": "Editor livelli",
"Map view": "Vista mappa",
"Maputnik on GitHub": "Maputnik su GitHub",
"Open": "Apri",
"Save": "Salva",
"Data Sources": "Fonti dati",
"Style Settings": "Impostazioni stile",
"View": "Vista",
"Color accessibility": "Accessibilità colori",
"Help": "Aiuto",
"Comments for the current layer. This is non-standard and not in the spec.": "Commenti per il livello attuale. Questo non è standard e non è nella specifica.",
"Comments": "Commenti",
"Comment...": "Il tuo commento...",
"Max Zoom": "Zoom massimo",
"Min Zoom": "Zoom minimo",
"Source": "Sorgente",
"Source Layer": "Livello sorgente",
"Type": "Tipo",
"Nested filters are not supported.": "I filtri nidificati non sono supportati.",
"Upgrade to expression": "Aggiorna a espressione",
"Filter": "Filtro",
"every filter matches": "tutti i filtri corrispondono",
"no filter matches": "nessun filtro corrisponde",
"any filter matches": "qualsiasi filtro corrisponde",
"Add filter": "Aggiungi filtro",
"You've entered an old style filter.": "Hai inserito uno stile filtro obsoleto.",
"Switch to filter editor.": "Passa all'editor dei filtri.",
"Delete filter block": "Elimina blocco filtro",
"Add value": "Aggiungi valore",
"Remove array item": "Rimuovi elemento dall'array",
"Press <1>ESC</1> to lose focus": "Premi <1>ESC</1> per perdere il focus",
"Must provide protocol: <1>https://</1>": "Protocollo richiesto: <1>https://</1>",
"Must provide protocol: <1>http://</1> or <3>https://</3>": "Protocollo richiesto: <1>http://</1> o <3>https://</3>",
"CORS policy won't allow fetching resources served over http from https, use a <1>https://</1> domain": "La politica CORS non permette di caricare risorse tramite http da https, utilizza un dominio <1>https://</1>.",
"Layer": "Livello",
"JSON Editor": "Editor JSON",
"Delete": "Elimina",
"Duplicate": "Duplica",
"Show": "Mostra",
"Hide": "Nascondi",
"Move layer up": "Sposta il livello in su",
"Move layer down": "Sposta il livello in giù",
"Layer: {{layerId}}": "Livello: {{layerId}}",
"Layers": "Livelli",
"Collapse": "Coprimi",
"Expand": "Espandi",
"Add Layer": "Aggiungi Livello",
"Search": "Cerca",
"Zoom:": "Zoom:",
"Close popup": "Chiudi popup",
"cursor:": "cursore:",
"center:": "centro:",
"rotation:": "rotazione:",
"Close modal": "Chiudi finestra modale",
"Debug": "Debug",
"Options": "Opzioni",
"Save Style": "Opzioni stile",
"Save the JSON style to your computer.": "Salva lo style JSON nel tuo computer.",
"Save as": "Salva con nome",
"Create HTML": "Crea HTML",
"Cancel": "Annulla",
"Open Style": "Apri stile",
"Open local Style": "Apri stile locale",
"Open a local JSON style from your computer.": "Apri uno stile JSON dal tuo computer.",
"Load from URL": "Carica da URL",
"Load from a URL. Note that the URL must have <1>CORS enabled</1>.": "Carica da URL. Nota che l'URL deve avere l'abilitazione <1>CORS attivata</1>.",
"Style URL": "Stile URL",
"Enter URL...": "Inserisci URL...",
"Gallery Styles": "Galleria degli stili",
"Open one of the publicly available styles to start from.": "Apro uno degli stili pubblici a partire da.",
"Loading style": "Caricamento stile",
"Loading: {{requestUrl}}": "Caricamento: {{requestUrl}}",
"Name": "Nome",
"Owner": "Proprietario",
"Owner ID of the style. Used by Mapbox or future style APIs.": "ID del proprietario dello stile. Utilizzato da Mapbox o dalle future API di stile.",
"Sprite URL": "URL Sprite",
"Glyphs URL": "URL Glyphs",
"Center": "Centro",
"Bearing": "Direzione",
"Pitch": "Inclinazione",
"Light anchor": "Ancora della luce",
"Light color": "Colore della luce",
"Light intensity": "Intensità della luce",
"Light position": "Posizione della luce",
"Terrain source": "Sorgente del terreno",
"Terrain exaggeration": "Esagerazione del terreno",
"Transition delay": "Ritardo della transizione",
"Transition duration": "Durate della transizione",
"Open Layers (experimental)": "Apri livelli (sperimentale)",
"Shortcuts menu": "Scorciatoie del menu",
"Open modal": "Apri finestra modale",
"Export modal": "Esporta finestra modale",
"Data Sources modal": "Finestra modale sorgente dati",
"Style Settings modal": "Finestra modale impostazione degli stili",
"Toggle inspect": "Attiva/disattiva ispezione",
"Focus map": "Metti a fuoco la mappa",
"Debug modal": "Finestra modale di debug",
"Increase the zoom level by 1.": "Aumenta il livello di zoom di 1.",
"Increase the zoom level by 2.": "Aumenta il livello di zoom di 2.",
"Decrease the zoom level by 1.": "Riduci il livello di zoom di 1.",
"Decrease the zoom level by 2.": "Riduci il livello di zoom di 1..",
"Pan up by 100 pixels.": "Sposta in alto di 100 pixels.",
"Pan down by 100 pixels.": "Sposta in basso di 100 pixels",
"Pan left by 100 pixels.": "Sposta a sinistra di 100 pixels",
"Pan right by 100 pixels.": "Sposta a destra di 100 pixels",
"Increase the rotation by 15 degrees.": "Incrementa la rotazione di 15 gradi.",
"Decrease the rotation by 15 degrees.": "Aumenta la rotazione di 15 gradi.",
"Increase the pitch by 10 degrees.": "Incrementa la rotazione di 10 gradi",
"Decrease the pitch by 10 degrees.": "Aumenta la rotazione di 10 gradi.",
"Shortcuts": "Scorciatoie",
"Press <1>ESC</1> to lose focus of any active elements, then press one of:": "Premi <1>ESC</1> per perdere il focus su qualsiasi elemento attivo, poi premi uno dei seguenti tasti:",
"If the Map is in focused you can use the following shortcuts": "Se la mappa è attiva, puoi utilizzare le seguenti scorciatoie:",
"Remove '{{sourceId}}' source": "Rimuovi sorgente '{{sourceId}}'",
"Source ID": "ID sorgente",
"Unique ID that identifies the source and is used in the layer to reference the source.": "ID univoco che identifica la sorgente ed è utilizzato nel livello per fare riferimento alla sorgente.",
"Source Type": "Tipo sorgente",
"GeoJSON (JSON)": "GeoJSON (JSON)",
"GeoJSON (URL)": "GeoJSON (URL)",
"Vector (TileJSON URL)": "Vettore (TileJSON URL)",
"Vector (Tile URLs)": "Vettore (Tile URLs)",
"Raster (TileJSON URL)": "Raster (TileJSON URL)",
"Raster (Tile URLs)": "Raster (Tile URLs)",
"Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)",
"Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)",
"Vector (PMTiles)": "Vettore (PMTiles)",
"Image": "Immagine",
"Video": "Video",
"Add Source": "Aggiungi sorgente",
"Sources": "Sorgenti",
"Active Sources": "Sorgenti attive",
"Choose Public Source": "Scegli una sorgente pubblica",
"Add one of the publicly available sources to your style.": "Aggiungi una delle sorgente pubbliche disponibili al tuo stile.",
"Add New Source": "Aggiungi nuova sorgente",
"Add a new source to your style. You can only choose the source type and id at creation time!": "Aggiungi una nuova sorgente al tuo stile. Puoi scegliere il tipo di sorgente e l'ID solo al momento della creazione!",
"TileJSON URL": "TileJSON URL",
"Tile URL": "Tile-URL",
"Scheme Type": "Tipo di schema",
"Coord top left": "Coordinate in alto a sinistra",
"Coord top right": "Coordinate in alto a destra",
"Coord bottom right": "Coordinate in basso a destra",
"Coord bottom left": "Coordinate in basso a sinistra",
"Image URL": "Indirizzo immagine",
"Video URL": "Indirizzo video",
"GeoJSON URL": "GeoJSON URL",
"GeoJSON": "GeoJSON",
"Cluster": "Cluster",
"PMTiles URL": "PMTiles URL",
"Tile Size": "Dimensioni tile",
"Encoding": "Codifica",
"Error:": "Errore:",
"MapTiler Access Token": "Accesso token MapTiler",
"Public access token for MapTiler Cloud.": "Token di accesso pubblico per MapTiler Cloud.",
"Learn More": "Scopri di più",
"Thunderforest Access Token": "Accesso token Thunderforest",
"Public access token for Thunderforest services.": "Token di accesso pubblico per i servizi Thunderforest.",
"Stadia Maps API Key": "Chiave API di Stadia Maps.",
"API key for Stadia Maps.": "Chiave API per Stadia Maps.",
"Style Renderer": "Renderer dello stile",
"Choose the default Maputnik renderer for this style.": "Scegli il renderer predefinito di Maputnik per questo stile.",
"Language": "Lingua",
"Press <1>ESC</1> to lose focus of any input box": "Premi <1>ESC</1> per perdere il focus su qualsiasi campo di input."
}

View File

@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "ラスタ (Tile URLs)",
"Raster DEM (TileJSON URL)": "ラスタ DEM (TileJSON URL)",
"Raster DEM (XYZ URLs)": "ラスタ DEM (XYZ URL)",
"Vector (PMTiles)": "ベクトル (PMTiles)",
"Image": "画像",
"Video": "動画",
"Add Source": "ソースを追加",
@@ -170,6 +171,7 @@
"GeoJSON URL": "GeoJSON URL",
"GeoJSON": "GeoJSON",
"Cluster": "クラスタ",
"PMTiles URL": "PMTiles URL",
"Tile Size": "タイルサイズ",
"Encoding": "エンコーディング",
"Error:": "エラー:",

View File

@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "栅格数据 (Tile URLs)",
"Raster DEM (TileJSON URL)": "栅格高程数据 (TileJSON URL)",
"Raster DEM (XYZ URLs)": "栅格高程数据 (XYZ URLs)",
"Vector (PMTiles)": "__STRING_NOT_TRANSLATED__",
"Image": "图像",
"Video": "视频",
"Add Source": "添加源",
@@ -170,6 +171,7 @@
"GeoJSON URL": "GeoJSON URL",
"GeoJSON": "GeoJSON",
"Cluster": "聚合",
"PMTiles URL": "__STRING_NOT_TRANSLATED__",
"Tile Size": "__STRING_NOT_TRANSLATED__",
"Encoding": "编码",
"Error:": "错误:",

View File

@@ -88,4 +88,3 @@
.maplibregl-ctrl-map {
background-image: url('data:image/svg+xml;charset=utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="#8e8e8e%22%20viewBox=%22-10%20-10%2060%2060%22%20preserveAspectRatio=%22xMidYMid%20meet%22%3E%3Cg%3E%3Cpath%20d=%22m25%2031.640000000000004v-19.766666666666673l-10-3.511666666666663v19.766666666666666z%20m9.140000000000008-26.640000000000004q0.8599999999999923%200%200.8599999999999923%200.8600000000000003v25.156666666666666q0%200.625-0.625%200.783333333333335l-9.375%203.1999999999999993-10-3.5133333333333354-8.906666666666668%203.4383333333333326-0.2333333333333334%200.07833333333333314q-0.8616666666666664%200-0.8616666666666664-0.8599999999999994v-25.156666666666663q0-0.625%200.6233333333333331-0.7833333333333332l9.378333333333334-3.198333333333334%2010%203.5133333333333336%208.905000000000001-3.4383333333333344z%22%3E%3C/path%3E%3C/g%3E%3C/svg%3E');
}

View File

@@ -1,3 +1,5 @@
@use "vars";
@font-face {
font-family: 'Roboto';
src: url('../fonts/Roboto-Regular.ttf') format('truetype');
@@ -15,8 +17,8 @@
}
html {
color: $color-white;
font-size: $font-size-5;
color: vars.$color-white;
font-size: vars.$font-size-5;
box-sizing: border-box;
}
@@ -32,34 +34,34 @@ body {
}
p {
font-size: $font-size-6;
margin-top: $margin-2;
margin-bottom: $margin-2;
color: $color-lowgray;
font-size: vars.$font-size-6;
margin-top: vars.$margin-2;
margin-bottom: vars.$margin-2;
color: vars.$color-lowgray;
line-height: 1.3;
}
h1 {
font-size: $font-size-2;
margin-bottom: $margin-3;
font-size: vars.$font-size-2;
margin-bottom: vars.$margin-3;
font-weight: bold;
}
h2 {
font-size: $font-size-3;
margin-bottom: $margin-3;
font-size: vars.$font-size-3;
margin-bottom: vars.$margin-3;
font-weight: bold;
}
h3 {
font-size: $font-size-4;
margin-bottom: $margin-3;
font-size: vars.$font-size-4;
margin-bottom: vars.$margin-3;
font-weight: bold;
}
h4 {
font-size: $font-size-5;
margin-bottom: $margin-3;
font-size: vars.$font-size-5;
margin-bottom: vars.$margin-3;
}
input:focus,
@@ -68,12 +70,12 @@ textarea:focus,
button:focus,
.maputnik-toolbar-link:focus,
select:focus {
color: $color-white;
color: vars.$color-white;
outline: #8e8e8e auto 1px;
}
label:hover {
color: $color-white;
color: vars.$color-white;
}
.clearfix {

View File

@@ -1,3 +1,5 @@
@use "vars";
.CodeMirror-lint-tooltip {
z-index: 2000 !important;
}
@@ -55,7 +57,7 @@
.cm-s-maputnik .CodeMirror-matchingbracket {
background: hsla(223, 12%, 35%, 1);
color: $color-white !important;
color: vars.$color-white !important;
}
.cm-s-maputnik .CodeMirror-nonmatchingbracket {
@@ -69,7 +71,7 @@
}
to {
opacity: 0;
}
}
}
.JSONEditor__message {
@@ -79,7 +81,7 @@
z-index: 99999;
padding: 0.3em 0.5em;
background: hsla(0, 0%, 0%, 0.3);
color: $color-lowgray;
color: vars.$color-lowgray;
border-bottom-left-radius: 2px;
transition: opacity 320ms ease;
opacity: 0;

View File

@@ -1,9 +1,12 @@
@use "mixins";
@use "vars";
@use 'sass:color';
// MAP
.maputnik-map__container {
background: white;
display: flex;
width: $layout-map-width;
width: vars.$layout-map-width;
&--error {
align-items: center;
@@ -36,7 +39,7 @@
&-wrapper {
display: inline-block;
box-sizing: border-box;
font-size: $font-size-6;
font-size: vars.$font-size-6;
line-height: 2;
user-select: none;
position: relative;
@@ -45,9 +48,9 @@
&-popup {
display: none;
color: $color-lowgray;
background-color: $color-gray;
padding: $margin-2;
color: vars.$color-lowgray;
background-color: vars.$color-gray;
padding: vars.$margin-2;
font-size: 10px;
position: absolute;
top: 20px;
@@ -60,7 +63,7 @@
&-button {
opacity: 0;
pointer-events: none;
background: $color-black;
background: vars.$color-black;
color: white;
border: none;
padding: 4px;
@@ -77,11 +80,11 @@
}
.maputnik-doc-inline {
color: $color-lowgray;
background-color: $color-gray;
padding: $margin-2;
color: vars.$color-lowgray;
background-color: vars.$color-gray;
padding: vars.$margin-2;
font-size: 12px;
margin-top: $margin-3;
margin-top: vars.$margin-3;
line-height: 1.5;
flex: 1 0;
}
@@ -95,10 +98,10 @@
.maputnik-button {
display: inline-block;
cursor: pointer;
background-color: $color-midgray;
color: $color-lowgray;
font-size: $font-size-6;
padding: $margin-2;
background-color: vars.$color-midgray;
color: vars.$color-lowgray;
font-size: vars.$font-size-6;
padding: vars.$margin-2;
user-select: none;
border-width: 0;
border-radius: 2px;
@@ -106,36 +109,36 @@
text-decoration: none;
&:hover {
background-color: color.adjust($color-midgray, $lightness: 12%);
color: $color-white;
background-color: color.adjust(vars.$color-midgray, $lightness: 12%);
color: vars.$color-white;
}
&:disabled {
background-color: color.adjust($color-midgray, $lightness: -5%);
color: $color-midgray;
background-color: color.adjust(vars.$color-midgray, $lightness: -5%);
color: vars.$color-midgray;
cursor: not-allowed;
}
}
.maputnik-big-button {
margin-top: $margin-3;
margin-top: vars.$margin-3;
display: inline-block;
padding: $margin-3;
font-size: $font-size-5;
padding: vars.$margin-3;
font-size: vars.$font-size-5;
}
.maputnik-wide-button {
padding: $margin-2 $margin-3;
padding: vars.$margin-2 vars.$margin-3;
}
.maputnik-green-button {
background-color: $color-green;
color: $color-black;
background-color: vars.$color-green;
color: vars.$color-black;
}
.maputnik-white-button {
background-color: $color-white;
color: $color-black;
background-color: vars.$color-white;
color: vars.$color-black;
}
.maputnik-icon-button {
@@ -150,19 +153,19 @@
}
svg {
fill: $color-white;
fill: vars.$color-white;
}
}
}
// INPUT BLOCK
.maputnik-input-block {
margin: $margin-3;
margin: vars.$margin-3;
display: flex;
flex-wrap: wrap;
&-label {
color: $color-lowgray;
color: vars.$color-lowgray;
user-select: none;
width: 32%;
vertical-align: top;
@@ -170,7 +173,7 @@
}
&-action {
color: $color-lowgray;
color: vars.$color-lowgray;
user-select: none;
width: 18%;
vertical-align: top;
@@ -187,7 +190,7 @@
.maputnik-input-block-label {
display: inline-block;
width: 32%;
margin-bottom: $margin-3;
margin-bottom: vars.$margin-3;
}
.maputnik-input-block-action {
@@ -211,15 +214,15 @@
// SPACE HELPER
.maputnik-space {
@include vendor-prefix(flex-grow, 1);
@include mixins.vendor-prefix(flex-grow, 1);
}
// MESSAGE PANEL
.maputnik-message-panel {
padding: $margin-2;
padding: vars.$margin-2;
&-error {
color: $color-red;
color: vars.$color-red;
}
&__switch-button {
@@ -241,7 +244,7 @@
&__menu {
position: absolute;
z-index: 999999;
background: $color-black;
background: vars.$color-black;
display: flex;
flex-direction: column;
align-content: stretch;
@@ -262,13 +265,13 @@
color: #a4a4a4;
padding: 0.4em 0.4em;
font-size: 0.9em;
border: solid 1px $color-red;
border: solid 1px vars.$color-red;
border-radius: 2px;
margin: $margin-2 0px;
margin: vars.$margin-2 0px;
}
.maputnik-expression-editor {
border: solid 1px $color-gray;
border: solid 1px vars.$color-gray;
}
.maputnik-input-block--wide {
@@ -288,13 +291,13 @@
}
.maputnik-expr-infobox {
font-size: $font-size-6;
background: $color-midgray;
padding: $margin-2;
font-size: vars.$font-size-6;
background: vars.$color-midgray;
padding: vars.$margin-2;
border-radius: 2px;
border-top-right-radius: 0px;
border-top-left-radius: 0px;
color: $color-white;
color: vars.$color-white;
}
.maputnik-expr-infobox__button {
@@ -305,4 +308,3 @@
color: currentColor;
cursor: pointer;
}

View File

@@ -1,5 +1,7 @@
@use "vars";
.maputnik-filter-editor-wrapper {
padding: $margin-3;
padding: vars.$margin-3;
overflow: hidden;
.maputnik-input-block {
@@ -8,8 +10,8 @@
}
.maputnik-filter-editor {
@extend .clearfix; /* stylelint-disable-line */
color: $color-lowgray;
@extend .clearfix !optional; /* stylelint-disable-line */
color: vars.$color-lowgray;
}
.maputnik-filter-editor-property {
@@ -39,7 +41,7 @@
}
.maputnik-filter-editor-compound-select {
margin-bottom: $margin-2;
margin-bottom: vars.$margin-2;
.maputnik-doc-wrapper {
width: 50%;
@@ -52,22 +54,22 @@
}
.maputnik-filter-editor-unsupported {
color: $color-midgray;
color: vars.$color-midgray;
}
.maputnik-add-filter {
display: inline-block;
float: right;
margin-top: $margin-3;
margin-top: vars.$margin-3;
}
.maputnik-delete-filter {
@extend .maputnik-icon-button; /* stylelint-disable-line */
@extend .maputnik-icon-button !optional; /* stylelint-disable-line */
}
.maputnik-filter-editor-block-action {
margin-top: $margin-2;
margin-bottom: $margin-2;
margin-top: vars.$margin-2;
margin-bottom: vars.$margin-2;
display: inline-block;
width: 6%;
margin-right: 1.5%;
@@ -79,12 +81,12 @@
}
.maputnik-radio-as-button {
@extend .maputnik-button; /* stylelint-disable-line */
@extend .maputnik-button !optional; /* stylelint-disable-line */
border: solid 1px transparent;
&:focus-within {
border: solid 1px $color-white;
border: solid 1px vars.$color-white;
}
input {

View File

@@ -1,3 +1,6 @@
@use "mixins";
@use "vars";
@use 'sass:color';
//INPUT
.maputnik-input {
@@ -5,13 +8,13 @@
width: 100%;
display: block;
box-sizing: border-box;
font-size: $font-size-6;
font-size: vars.$font-size-6;
line-height: 2;
padding-left: $margin-2;
padding-right: $margin-2;
padding-left: vars.$margin-2;
padding-right: vars.$margin-2;
border: none;
background-color: $color-gray;
color: color.adjust($color-lowgray, $lightness: 12%);
background-color: vars.$color-gray;
color: color.adjust(vars.$color-lowgray, $lightness: 12%);
&:invalid {
border: solid 1px #B71C1C;
@@ -20,7 +23,7 @@
}
.maputnik-string {
@extend .maputnik-input; /* stylelint-disable-line */
@extend .maputnik-input !optional; /* stylelint-disable-line */
&--multi {
resize: vertical;
@@ -44,12 +47,12 @@
}
.maputnik-number {
@extend .maputnik-input; /* stylelint-disable-line */
@extend .maputnik-input !optional; /* stylelint-disable-line */
}
//COLOR PICKER
.maputnik-color {
@extend .maputnik-input; /* stylelint-disable-line */
@extend .maputnik-input !optional; /* stylelint-disable-line */
height: 26px;
}
@@ -57,7 +60,7 @@
.maputnik-color-wrapper {
position: relative;
@include flex-row;
@include mixins.flex-row;
}
.maputnik-color-swatch {
@@ -71,7 +74,7 @@
.maputnik-array {
> * {
margin-bottom: $margin-3;
margin-bottom: vars.$margin-3;
}
.maputnik-array-block {
@@ -96,19 +99,19 @@
// SELECT
.maputnik-select {
@extend .maputnik-input; /* stylelint-disable-line */
@extend .maputnik-input !optional; /* stylelint-disable-line */
-moz-appearance: none;
-webkit-appearance: none;
background: $color-gray url("#{$icon-down-arrow}") right center no-repeat;
color: $color-white;
background: vars.$color-gray url("#{vars.$icon-down-arrow}") right center no-repeat;
color: vars.$color-white;
background-position: calc(100% - 2px) center;
padding-right: 20px;
height: 24px;
}
[dir="rtl"] .maputnik-select {
background: $color-gray url("#{$icon-down-arrow}") left center no-repeat;
background: vars.$color-gray url("#{vars.$icon-down-arrow}") left center no-repeat;
}
// MULTIBUTTON
@@ -116,12 +119,12 @@
padding: 0;
.maputnik-button {
margin-right: $margin-1;
margin-right: vars.$margin-1;
}
}
.maputnik-button-selected {
background-color: color.adjust($color-midgray, $lightness: 12%);
background-color: color.adjust(vars.$color-midgray, $lightness: 12%);
color: white;
}
@@ -137,7 +140,7 @@
outline: none;
&-wrapper {
@extend .maputnik-input; /* stylelint-disable-line */
@extend .maputnik-input !optional; /* stylelint-disable-line */
padding-left: 0;
padding-right: 0;
@@ -154,12 +157,12 @@
text-align: center;
height: 24px;
width: 24px;
margin-right: $margin-2;
background-color: $color-gray;
margin-right: vars.$margin-2;
background-color: vars.$color-gray;
border-radius: 2px;
border-style: solid;
border-width: 2px;
border-color: $color-gray;
border-color: vars.$color-gray;
transition: background-color 0.1s ease-out;
position: absolute;
top: 0;
@@ -185,7 +188,7 @@
width: 50%;
height: 50%;
margin-top: 1px;
fill: $color-lowgray;
fill: vars.$color-lowgray;
}
}
@@ -198,29 +201,29 @@
position: absolute;
overflow: auto;
max-height: 50%;
background: $color-gray;
background: vars.$color-gray;
z-index: 3;
}
&-menu-item {
user-select: none;
color: $color-lowgray;
color: vars.$color-lowgray;
cursor: default;
padding: $margin-1;
font-size: $font-size-6;
padding: vars.$margin-1;
font-size: vars.$font-size-6;
z-index: 3;
background: $color-gray;
background: vars.$color-gray;
}
&-menu-item-selected {
background: $color-midgray;
background: vars.$color-midgray;
}
}
// FONT
.maputnik-font {
.maputnik-autocomplete:not(:last-child) {
margin-bottom: $margin-3;
margin-bottom: vars.$margin-3;
}
}
@@ -236,21 +239,21 @@
.SpecDoc__sdk-support__table {
width: 100%;
margin-top: $margin-3;
margin-top: vars.$margin-3;
td, th {
border: solid 1px $color-midgray;
border: solid 1px vars.$color-midgray;
padding: 4px 6px;
white-space: nowrap;
}
}
.SpecDoc__values li {
margin-top: $margin-3;
margin-top: vars.$margin-3;
}
.SpecDoc__values code {
background: $color-midgray;
background: vars.$color-midgray;
padding: 0.1em 0.3em;
border-radius: 2px;
}

View File

@@ -1,3 +1,6 @@
@use "mixins";
@use "vars";
@use 'sass:color';
// LAYER LIST
.maputnik-layer-list {
@@ -7,9 +10,9 @@
flex-direction: column;
&-header {
padding: $margin-2 $margin-2 $margin-3;
padding: vars.$margin-2 vars.$margin-2 vars.$margin-3;
@include flex-row;
@include mixins.flex-row;
flex: 0 0;
> * {
@@ -19,8 +22,8 @@
}
&-header-title {
font-size: $font-size-5;
color: $color-white;
font-size: vars.$font-size-5;
color: vars.$color-white;
font-weight: bold;
line-height: 1.3;
}
@@ -28,7 +31,7 @@
&-container {
padding: 0;
margin: 0;
padding-bottom: $margin-5;
padding-bottom: vars.$margin-5;
flex: 1;
overflow-x: hidden;
overflow-y: auto;
@@ -47,9 +50,9 @@
&-item {
border: solid 1px transparent;
font-weight: 400;
color: $color-lowgray;
font-size: $font-size-6;
border-bottom-color: color.adjust($color-black, $lightness: 0.1%);
color: vars.$color-lowgray;
font-size: vars.$font-size-6;
border-bottom-color: color.adjust(vars.$color-black, $lightness: 0.1%);
user-select: none;
list-style: none;
z-index: 2000;
@@ -63,21 +66,22 @@
transition: opacity 600ms, visibility 600ms;
&:focus-within {
border: solid 1px $color-lowgray;
border: solid 1px vars.$color-lowgray;
}
@include mixins.flex-row;
@media screen and (prefers-reduced-motion: reduce) {
transition-duration: 0;
}
@include flex-row;
}
&-icon-action {
display: none;
svg {
fill: $color-black;
fill: vars.$color-black;
}
}
@@ -88,10 +92,10 @@
height: 15px;
svg {
fill: color.adjust($color-lowgray, $lightness: -20%);
fill: color.adjust(vars.$color-lowgray, $lightness: -20%);
&:hover {
fill: $color-white;
fill: vars.$color-white;
}
}
}
@@ -102,24 +106,24 @@
.maputnik-layer-list-item:hover,
.maputnik-layer-list-item-selected {
background-color: color.adjust($color-black, $lightness: 2%);
background-color: color.adjust(vars.$color-black, $lightness: 2%);
.maputnik-layer-list-icon-action {
display: block;
svg {
fill: color.adjust($color-lowgray, $lightness: -0.5%);
fill: color.adjust(vars.$color-lowgray, $lightness: -0.5%);
}
}
}
.maputnik-layer-list-item--error {
color: $color-red;
color: vars.$color-red;
}
&-item-selected {
color: $color-white;
color: vars.$color-white;
}
&-item-collapsed {
@@ -132,7 +136,7 @@
}
&-item-group-last {
border-bottom: 2px solid $color-gray;
border-bottom: 2px solid vars.$color-gray;
}
&-item-id {
@@ -150,14 +154,14 @@
&-group-header {
border: solid 1px transparent;
font-size: $font-size-6;
color: $color-lowgray;
background-color: color.adjust($color-black, $lightness: 2%);
font-size: vars.$font-size-6;
color: vars.$color-lowgray;
background-color: color.adjust(vars.$color-black, $lightness: 2%);
user-select: none;
padding: $margin-2;
padding: vars.$margin-2;
&:focus-within {
border: solid 1px $color-lowgray;
border: solid 1px vars.$color-lowgray;
}
button {
@@ -165,7 +169,7 @@
cursor: pointer;
}
@include flex-row;
@include mixins.flex-row;
svg {
width: 14px;
@@ -178,25 +182,27 @@
}
&-group-content {
margin: 0 $margin-3;
margin: 0 vars.$margin-3;
}
}
// FILTER EDITOR
.maputnik-layer-editor-group {
font-weight: bold;
font-size: $font-size-5;
background-color: color.adjust($color-black, $lightness: 2%);
color: $color-white;
font-size: vars.$font-size-5;
background-color: color.adjust(vars.$color-black, $lightness: 2%);
color: vars.$color-white;
cursor: pointer;
user-select: none;
line-height: 20px;
border-top: solid 1px #36383e;
@include mixins.flex-row;
&__button {
flex: 1;
display: flex;
padding: $margin-2;
padding: vars.$margin-2;
&__icon {
fill: white;
@@ -223,17 +229,17 @@
}
@include flex-row;
&:hover {
background-color: $color-gray;
background-color: vars.$color-gray;
}
}
// PROPERTY
.maputnik-default-property {
.maputnik-input-block-label {
color: color.adjust($color-lowgray, $lightness: -20%);
color: color.adjust(vars.$color-lowgray, $lightness: -20%);
}
.maputnik-string,
@@ -241,8 +247,8 @@
.maputnik-color,
.maputnik-select,
.maputnik-checkbox-wrapper {
background-color: color.adjust($color-gray, $lightness: -2%);
color: color.adjust($color-lowgray, $lightness: -20%);
background-color: color.adjust(vars.$color-gray, $lightness: -2%);
color: color.adjust(vars.$color-lowgray, $lightness: -20%);
}
.maputnik-make-zoom-function svg {
@@ -250,18 +256,18 @@
}
.maputnik-multibutton .maputnik-button {
background-color: color.adjust($color-midgray, $lightness: -10%);
color: color.adjust($color-lowgray, $lightness: -20%);
background-color: color.adjust(vars.$color-midgray, $lightness: -10%);
color: color.adjust(vars.$color-lowgray, $lightness: -20%);
&:hover {
background-color: color.adjust($color-midgray, $lightness: 12%);
color: $color-white;
background-color: color.adjust(vars.$color-midgray, $lightness: 12%);
color: vars.$color-white;
}
}
.maputnik-multibutton .maputnik-button-selected {
background-color: color.adjust($color-midgray, $lightness: -2%);
color: $color-lowgray;
background-color: color.adjust(vars.$color-midgray, $lightness: -2%);
color: vars.$color-lowgray;
}
}
@@ -276,8 +282,8 @@
&__menu {
position: absolute;
z-index: 9999;
background: $color-black;
border: solid 1px $color-midgray;
background: vars.$color-black;
border: solid 1px vars.$color-midgray;
right: 0;
min-width: 120px;
}
@@ -295,7 +301,7 @@
.layer-header {
display: flex;
padding: 6px;
background: $color-black;
background: vars.$color-black;
&__title {
flex: 1;
@@ -310,7 +316,7 @@
// Clone of the element which is sorted
.sortableHelper {
font-family: $font-family;
font-family: vars.$font-family;
z-index: 9999;
border: none;
}

Some files were not shown because too many files have changed in this diff Show More