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
-46
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"
}
}
-1
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) --> <!-- 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) -->
-1
View File
@@ -8,4 +8,3 @@
- [ ] Include before/after visuals or gifs if this PR includes visual changes. - [ ] Include before/after visuals or gifs if this PR includes visual changes.
- [ ] Write tests for all new functionality. - [ ] Write tests for all new functionality.
- [ ] Add an entry to `CHANGELOG.md` under the `## main` section. - [ ] Add an entry to `CHANGELOG.md` under the `## main` section.
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
steps: steps:
- name: Dependabot metadata - name: Dependabot metadata
id: metadata id: metadata
uses: dependabot/fetch-metadata@v2.2.0 uses: dependabot/fetch-metadata@v2.3.0
with: with:
github-token: "${{ secrets.GITHUB_TOKEN }}" github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve Dependabot PRs - name: Approve Dependabot PRs
+48 -48
View File
@@ -14,8 +14,8 @@ jobs:
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: docker build -t test-docker-image-build . - run: docker build -t test-docker-image-build .
# build the editor # build the editor
build-node: build-node:
@@ -27,17 +27,17 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest] os: [ ubuntu-latest, windows-latest, macos-latest ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
- run: npm run lint - run: npm run lint
- run: npm run lint-css - run: npm run lint-css
@@ -47,53 +47,53 @@ jobs:
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
- name: artifacts/maputnik - name: artifacts/maputnik
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: maputnik name: maputnik
path: dist path: dist
# Build and upload desktop CLI artifacts # Build and upload desktop CLI artifacts
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.23.x go-version: ^1.23.x
cache-dependency-path: desktop/go.sum cache-dependency-path: desktop/go.sum
id: go id: go
- name: Build desktop artifacts - name: Build desktop artifacts
run: npm run build-desktop run: npm run build-desktop
- name: Artifacts/linux - name: Artifacts/linux
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: maputnik-linux name: maputnik-linux
path: ./desktop/bin/linux/ path: ./desktop/bin/linux/
- name: Artifacts/darwin - name: Artifacts/darwin
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: maputnik-darwin name: maputnik-darwin
path: ./desktop/bin/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: e2e-tests:
name: "E2E tests using ${{ matrix.browser }}" name: "E2E tests using ${{ matrix.browser }}"
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
browser: [chrome, firefox] browser: [ chrome ]
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
+70
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
+11 -11
View File
@@ -20,7 +20,7 @@ jobs:
- name: Install - name: Install
run: npm ci run: npm ci
- name: Build - name: Build
run: npm run build run: npm run build
- name: Upload to GitHub Pages - name: Upload to GitHub Pages
@@ -28,7 +28,7 @@ jobs:
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: dist publish_dir: dist
# publish docker to GitHub registry # publish docker to GitHub registry
deploy-docker: deploy-docker:
name: deploy/docker name: deploy/docker
@@ -40,12 +40,12 @@ jobs:
fail-fast: false fail-fast: false
steps: steps:
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: docker build -t ghcr.io/maplibre/maputnik:main . - run: docker build -t ghcr.io/maplibre/maputnik:main .
- run: docker push ghcr.io/maplibre/maputnik:main - run: docker push ghcr.io/maplibre/maputnik:main
+1 -2
View File
@@ -2,7 +2,7 @@ name: Release
on: on:
push: push:
branches: [main] branches: [ main ]
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -99,4 +99,3 @@ jobs:
allowUpdates: true allowUpdates: true
draft: false draft: false
prerelease: false prerelease: false
+24
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
+2 -1
View File
@@ -13,6 +13,8 @@
- _...Add new stuff here..._ - _...Add new stuff here..._
### 🐞 Bug fixes ### 🐞 Bug fixes
- Fix incorrect handing of network error response (#944)
- _...Add new stuff here..._ - _...Add new stuff here..._
## 2.1.1 ## 2.1.1
@@ -30,4 +32,3 @@
## 1.7.0 ## 1.7.0
- See release notes at https://maputnik.github.io/blog/2020/04/23/release-v1.7.0 - See release notes at https://maputnik.github.io/blog/2020/04/23/release-v1.7.0
-1
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, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
+1 -1
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). 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 ### 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. 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.
+1 -1
View File
@@ -1,6 +1,6 @@
# Build Scripts # 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). The scripts are borrowed from [maplibre/maplibre-gl-js](https://github.com/maplibre/maplibre-gl-js/tree/bc70bc559cea5c987fa1b79fd44766cef68bbe28/build).
+5 -6
View File
@@ -20,14 +20,14 @@ const changelog = fs.readFileSync(changelogPath, 'utf8');
*/ */
const regex = /^## (\d+\.\d+\.\d+.*?)\n(.+?)(?=\n^## \d+\.\d+\.\d+.*?\n)/gms; const regex = /^## (\d+\.\d+\.\d+.*?)\n(.+?)(?=\n^## \d+\.\d+\.\d+.*?\n)/gms;
let releaseNotes = []; const releaseNotes = [];
let match; let match;
// eslint-disable-next-line no-cond-assign // eslint-disable-next-line no-cond-assign
while (match = regex.exec(changelog)) { while (match = regex.exec(changelog)) {
releaseNotes.push({ releaseNotes.push({
'version': match[1], 'version': match[1],
'changelog': match[2].trim(), 'changelog': match[2].trim(),
}); });
} }
const latest = releaseNotes[0]; const latest = releaseNotes[0];
@@ -44,5 +44,4 @@ const templatedReleaseNotes = `${header}
${latest.changelog}`; ${latest.changelog}`;
// eslint-disable-next-line eol-last
process.stdout.write(templatedReleaseNotes.trimEnd()); process.stdout.write(templatedReleaseNotes.trimEnd());
+1 -1
View File
@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver"; import { MaputnikDriver } from "./maputnik-driver";
describe("accessibility", () => { describe("accessibility", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver(); const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter(); beforeAndAfter();
describe("skip links", () => { describe("skip links", () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver"; import { MaputnikDriver } from "./maputnik-driver";
describe("history", () => { describe("history", () => {
let { beforeAndAfter, when, get, then } = new MaputnikDriver(); const { beforeAndAfter, when, get, then } = new MaputnikDriver();
beforeAndAfter(); beforeAndAfter();
let undoKeyCombo: string; let undoKeyCombo: string;
+1 -1
View File
@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver"; import { MaputnikDriver } from "./maputnik-driver";
describe("i18n", () => { describe("i18n", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver(); const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter(); beforeAndAfter();
describe("language detector", () => { describe("language detector", () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { MaputnikDriver } from "./maputnik-driver"; import { MaputnikDriver } from "./maputnik-driver";
describe("keyboard", () => { describe("keyboard", () => {
let { beforeAndAfter, given, when, get, then } = new MaputnikDriver(); const { beforeAndAfter, given, when, get, then } = new MaputnikDriver();
beforeAndAfter(); beforeAndAfter();
describe("shortcuts", () => { describe("shortcuts", () => {
beforeEach(() => { beforeEach(() => {
+43 -14
View File
@@ -2,7 +2,7 @@ import { v1 as uuid } from "uuid";
import { MaputnikDriver } from "./maputnik-driver"; import { MaputnikDriver } from "./maputnik-driver";
describe("layers", () => { describe("layers", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver(); const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter(); beforeAndAfter();
beforeEach(() => { beforeEach(() => {
when.setStyle("both"); when.setStyle("both");
@@ -101,7 +101,7 @@ describe("layers", () => {
}); });
describe("background", () => { describe("background", () => {
it("add", () => { it("add", () => {
let id = when.modal.fillLayers({ const id = when.modal.fillLayers({
type: "background", type: "background",
}); });
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
@@ -117,7 +117,7 @@ describe("layers", () => {
describe("modify", () => { describe("modify", () => {
function createBackground() { function createBackground() {
// Setup // Setup
let id = uuid(); const id = uuid();
when.selectWithin("add-layer.layer-type", "background"); when.selectWithin("add-layer.layer-type", "background");
when.setValue("add-layer.layer-id.input", "background:" + id); when.setValue("add-layer.layer-id.input", "background:" + id);
@@ -139,11 +139,11 @@ describe("layers", () => {
describe("layer", () => { describe("layer", () => {
it("expand/collapse"); it("expand/collapse");
it("id", () => { it("id", () => {
let bgId = createBackground(); const bgId = createBackground();
when.click("layer-list-item:background:" + bgId); when.click("layer-list-item:background:" + bgId);
let id = uuid(); const id = uuid();
when.setValue("layer-editor.layer-id.input", "foobar:" + id); when.setValue("layer-editor.layer-id.input", "foobar:" + id);
when.click("min-zoom"); when.click("min-zoom");
@@ -219,7 +219,7 @@ describe("layers", () => {
describe("comments", () => { describe("comments", () => {
let bgId: string; let bgId: string;
let comment = "42"; const comment = "42";
beforeEach(() => { beforeEach(() => {
bgId = createBackground(); bgId = createBackground();
@@ -320,11 +320,11 @@ describe("layers", () => {
// TODO // TODO
it.skip("parse error", () => { it.skip("parse error", () => {
let bgId = createBackground(); const bgId = createBackground();
when.click("layer-list-item:background:" + bgId); when.click("layer-list-item:background:" + bgId);
let errorSelector = ".CodeMirror-lint-marker-error"; const errorSelector = ".CodeMirror-lint-marker-error";
then(get.elementByTestId(errorSelector)).shouldNotExist(); then(get.elementByTestId(errorSelector)).shouldNotExist();
when.click(".CodeMirror"); when.click(".CodeMirror");
@@ -339,7 +339,7 @@ describe("layers", () => {
describe("fill", () => { describe("fill", () => {
it("add", () => { it("add", () => {
let id = when.modal.fillLayers({ const id = when.modal.fillLayers({
type: "fill", type: "fill",
layer: "example", layer: "example",
}); });
@@ -361,7 +361,7 @@ describe("layers", () => {
describe("line", () => { describe("line", () => {
it("add", () => { it("add", () => {
let id = when.modal.fillLayers({ const id = when.modal.fillLayers({
type: "line", type: "line",
layer: "example", layer: "example",
}); });
@@ -385,7 +385,7 @@ describe("layers", () => {
describe("symbol", () => { describe("symbol", () => {
it("add", () => { it("add", () => {
let id = when.modal.fillLayers({ const id = when.modal.fillLayers({
type: "symbol", type: "symbol",
layer: "example", layer: "example",
}); });
@@ -404,7 +404,7 @@ describe("layers", () => {
describe("raster", () => { describe("raster", () => {
it("add", () => { it("add", () => {
let id = when.modal.fillLayers({ const id = when.modal.fillLayers({
type: "raster", type: "raster",
layer: "raster", layer: "raster",
}); });
@@ -423,7 +423,7 @@ describe("layers", () => {
describe("circle", () => { describe("circle", () => {
it("add", () => { it("add", () => {
let id = when.modal.fillLayers({ const id = when.modal.fillLayers({
type: "circle", type: "circle",
layer: "example", layer: "example",
}); });
@@ -442,7 +442,7 @@ describe("layers", () => {
describe("fill extrusion", () => { describe("fill extrusion", () => {
it("add", () => { it("add", () => {
let id = when.modal.fillLayers({ const id = when.modal.fillLayers({
type: "fill-extrusion", type: "fill-extrusion",
layer: "example", layer: "example",
}); });
@@ -494,4 +494,33 @@ describe("layers", () => {
).shouldBeVisible(); ).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');
});
});
}); });
+4 -4
View File
@@ -1,11 +1,11 @@
import { MaputnikDriver } from "./maputnik-driver"; import { MaputnikDriver } from "./maputnik-driver";
describe("map", () => { describe("map", () => {
let { beforeAndAfter, get, when, then } = new MaputnikDriver(); const { beforeAndAfter, get, when, then } = new MaputnikDriver();
beforeAndAfter(); beforeAndAfter();
describe("zoom level", () => { describe("zoom level", () => {
it("via url", () => { it("via url", () => {
let zoomLevel = 12.37; const zoomLevel = 12.37;
when.setStyle("geojson", zoomLevel); when.setStyle("geojson", zoomLevel);
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible(); then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible();
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText( then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText(
@@ -14,7 +14,7 @@ describe("map", () => {
}); });
it("via map controls", () => { it("via map controls", () => {
let zoomLevel = 12.37; const zoomLevel = 12.37;
when.setStyle("geojson", zoomLevel); when.setStyle("geojson", zoomLevel);
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible(); then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible();
when.clickZoomIn(); when.clickZoomIn();
@@ -23,7 +23,7 @@ describe("map", () => {
); );
}); });
}); });
describe("search", () => { describe("search", () => {
it('should exist', () => { it('should exist', () => {
then(get.searchControl()).shouldBeVisible(); then(get.searchControl()).shouldBeVisible();
+2 -2
View File
@@ -8,8 +8,8 @@ export default class ModalDriver {
fillLayers: (opts: { type: string; layer?: string; id?: string }) => { fillLayers: (opts: { type: string; layer?: string; id?: string }) => {
// Having logic in test code is an anti pattern. // Having logic in test code is an anti pattern.
// This should be splitted to multiple single responsibility functions // This should be splitted to multiple single responsibility functions
let type = opts.type; const type = opts.type;
let layer = opts.layer; const layer = opts.layer;
let id; let id;
if (opts.id) { if (opts.id) {
id = opts.id; id = opts.id;
+61 -8
View File
@@ -1,7 +1,8 @@
import { MaputnikDriver } from "./maputnik-driver"; import { MaputnikDriver } from "./maputnik-driver";
import tokens from "../../src/config/tokens.json" with {type: "json"};
describe("modals", () => { describe("modals", () => {
let { beforeAndAfter, when, get, then } = new MaputnikDriver(); const { beforeAndAfter, when, get, given, then } = new MaputnikDriver();
beforeAndAfter(); beforeAndAfter();
beforeEach(() => { beforeEach(() => {
@@ -25,7 +26,7 @@ describe("modals", () => {
describe("when click open url", () => { describe("when click open url", () => {
beforeEach(() => { beforeEach(() => {
let styleFileUrl = get.exampleFileUrl(); const styleFileUrl = get.exampleFileUrl();
when.setValue("modal:open.url.input", styleFileUrl); when.setValue("modal:open.url.input", styleFileUrl);
when.click("modal:open.url.button"); when.click("modal:open.url.button");
@@ -70,7 +71,7 @@ describe("modals", () => {
it("public source"); it("public source");
it("add new source", () => { it("add new source", () => {
let sourceId = "n1z2v3r"; const sourceId = "n1z2v3r";
when.setValue("modal:sources.add.source_id", sourceId); when.setValue("modal:sources.add.source_id", sourceId);
when.select("modal:sources.add.source_type", "tile_vector"); when.select("modal:sources.add.source_type", "tile_vector");
when.select("modal:sources.add.scheme_type", "tms"); 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", () => { it("add new raster source", () => {
let sourceId = "rastertest"; const sourceId = "rastertest";
when.setValue("modal:sources.add.source_id", sourceId); when.setValue("modal:sources.add.source_id", sourceId);
when.select("modal:sources.add.source_type", "tile_raster"); when.select("modal:sources.add.source_type", "tile_raster");
when.select("modal:sources.add.scheme_type", "xyz"); when.select("modal:sources.add.scheme_type", "xyz");
@@ -159,7 +178,7 @@ describe("modals", () => {
}); });
}); });
it("glyphs url", () => { 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.setValue("modal:settings.glyphs", glyphsUrl);
when.click("modal:settings.name"); when.click("modal:settings.name");
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
@@ -168,7 +187,7 @@ describe("modals", () => {
}); });
it("maptiler access token", () => { it("maptiler access token", () => {
let apiKey = "testing123"; const apiKey = "testing123";
when.setValue( when.setValue(
"modal:settings.maputnik:openmaptiles_access_token", "modal:settings.maputnik:openmaptiles_access_token",
apiKey apiKey
@@ -182,7 +201,7 @@ describe("modals", () => {
}); });
it("thunderforest access token", () => { it("thunderforest access token", () => {
let apiKey = "testing123"; const apiKey = "testing123";
when.setValue( when.setValue(
"modal:settings.maputnik:thunderforest_access_token", "modal:settings.maputnik:thunderforest_access_token",
apiKey apiKey
@@ -194,7 +213,7 @@ describe("modals", () => {
}); });
it("stadia access token", () => { it("stadia access token", () => {
let apiKey = "testing123"; const apiKey = "testing123";
when.setValue( when.setValue(
"modal:settings.maputnik:stadia_access_token", "modal:settings.maputnik:stadia_access_token",
apiKey apiKey
@@ -217,6 +236,40 @@ describe("modals", () => {
metadata: { "maputnik:renderer": "ol" }, 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", () => { describe("sources", () => {
@@ -15,4 +15,3 @@
} }
] ]
} }
+1 -1
View File
@@ -31,4 +31,4 @@
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf", "glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf", "sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
"layers": [] "layers": []
} }
+1 -1
View File
@@ -26,4 +26,4 @@
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf", "glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf", "sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
"layers": [] "layers": []
} }
+1 -1
View File
@@ -15,4 +15,4 @@
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf", "glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf", "sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
"layers": [] "layers": []
} }
+1 -1
View File
@@ -34,4 +34,4 @@
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element> // visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// } // }
// } // }
// } // }
+61
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
}
}
)
+2 -2
View File
@@ -1,6 +1,6 @@
export default { export default {
output: 'src/locales/$LOCALE/$NAMESPACE.json', 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. // 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. // We add these keys manually, so we don't want to remove them.
@@ -10,7 +10,7 @@ export default {
keySeparator: false, keySeparator: false,
namespaceSeparator: 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. // The default value is a string that indicates that the string is not translated.
return '__STRING_NOT_TRANSLATED__'; return '__STRING_NOT_TRANSLATED__';
} }
+1198 -1336
View File
File diff suppressed because it is too large Load Diff
+28 -29
View File
@@ -9,7 +9,7 @@
"build": "tsc && vite build --base=/maputnik/", "build": "tsc && vite build --base=/maputnik/",
"build-desktop": "tsc && vite build --base=/ && cd desktop && make", "build-desktop": "tsc && vite build --base=/ && cd desktop && make",
"i18n:refresh": "i18next 'src/**/*.{ts,tsx,js,jsx}'", "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", "test": "cypress run",
"cy:open": "cypress open", "cy:open": "cypress open",
"lint-css": "stylelint \"src/styles/*.scss\"", "lint-css": "stylelint \"src/styles/*.scss\"",
@@ -29,20 +29,20 @@
"@maplibre/maplibre-gl-style-spec": "^23.1.0", "@maplibre/maplibre-gl-style-spec": "^23.1.0",
"@mdi/js": "^7.4.47", "@mdi/js": "^7.4.47",
"@mdi/react": "^1.6.1", "@mdi/react": "^1.6.1",
"@prantlf/jsonlint": "^16.0.0",
"array-move": "^4.0.0", "array-move": "^4.0.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"codemirror": "^5.65.2", "codemirror": "^5.65.18",
"color": "^4.2.3", "color": "^5.0.0",
"detect-browser": "^5.3.0", "detect-browser": "^5.3.0",
"events": "^3.3.0", "events": "^3.3.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"i18next": "^24.2.1", "i18next": "^24.2.2",
"i18next-browser-languagedetector": "^8.0.2", "i18next-browser-languagedetector": "^8.0.4",
"i18next-resources-to-backend": "^1.2.1", "i18next-resources-to-backend": "^1.2.1",
"json-stringify-pretty-compact": "^4.0.0", "json-stringify-pretty-compact": "^4.0.0",
"json-to-ast": "^2.1.0", "json-to-ast": "^2.1.0",
"jsonlint": "github:josdejong/jsonlint#85a19d7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash.capitalize": "^4.2.1", "lodash.capitalize": "^4.2.1",
"lodash.clamp": "^4.0.3", "lodash.clamp": "^4.0.3",
@@ -50,10 +50,11 @@
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"maplibre-gl": "^5.0.1", "maplibre-gl": "^5.1.1",
"maputnik-design": "github:maputnik/design#172b06c", "maputnik-design": "github:maputnik/design#172b06c",
"ol": "^10.3.1", "ol": "^10.4.0",
"ol-mapbox-style": "^12.4.0", "ol-mapbox-style": "^12.5.0",
"pmtiles": "^4.3.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-accessible-accordion": "^5.0.0", "react-accessible-accordion": "^5.0.0",
@@ -92,15 +93,16 @@
} }
}, },
"devDependencies": { "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", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@rollup/plugin-replace": "^6.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/codemirror": "^5.60.15",
"@types/color": "^4.2.0", "@types/color": "^4.2.0",
"@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.14", "@types/geojson": "^7946.0.16",
"@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",
@@ -108,7 +110,6 @@
"@types/lodash.get": "^4.4.9", "@types/lodash.get": "^4.4.9",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/lodash.throttle": "^4.1.9", "@types/lodash.throttle": "^4.1.9",
"@types/mocha": "^10.0.10",
"@types/randomcolor": "^0.5.9", "@types/randomcolor": "^0.5.9",
"@types/react": "^18.2.67", "@types/react": "^18.2.67",
"@types/react-aria-menubutton": "^6.2.14", "@types/react-aria-menubutton": "^6.2.14",
@@ -122,29 +123,27 @@
"@types/string-hash": "^1.1.3", "@types/string-hash": "^1.1.3",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"@types/wicg-file-system-access": "^2023.10.5", "@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", "@vitejs/plugin-react": "^4.3.4",
"cors": "^2.8.5", "cors": "^2.8.5",
"cypress": "^14.0.0", "cypress": "^14.1.0",
"cypress-plugin-tab": "^1.0.5", "cypress-plugin-tab": "^1.0.5",
"eslint": "^8.57.0", "eslint": "^9.21.0",
"eslint-plugin-react": "^7.37.4", "eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.18", "eslint-plugin-react-refresh": "^0.4.19",
"i18next-parser": "^9.1.0", "i18next-parser": "^9.3.0",
"istanbul": "^0.4.5", "istanbul": "^0.4.5",
"istanbul-lib-coverage": "^3.2.2", "istanbul-lib-coverage": "^3.2.2",
"mocha": "^11.0.1", "postcss": "^8.5.1",
"postcss": "^8.4.38",
"react-hot-loader": "^4.13.1", "react-hot-loader": "^4.13.1",
"sass": "^1.72.0", "sass": "^1.85.1",
"stylelint": "^16.13.2", "stylelint": "^16.15.0",
"stylelint-config-recommended-scss": "^14.1.0", "stylelint-config-recommended-scss": "^14.1.0",
"stylelint-scss": "^6.10.1", "stylelint-scss": "^6.11.1",
"typescript": "^5.4.3", "typescript": "^5.8.2",
"uuid": "^11.0.5", "typescript-eslint": "^8.25.0",
"vite": "^6.0.11", "uuid": "^11.1.0",
"vite-plugin-istanbul": "^6.0.2" "vite": "^6.2.0",
"vite-plugin-istanbul": "^7.0.0"
} }
} }
+55 -25
View File
@@ -8,6 +8,7 @@ import get from 'lodash.get'
import {unset} from 'lodash' import {unset} from 'lodash'
import {arrayMoveMutable} from 'array-move' import {arrayMoveMutable} from 'array-move'
import hash from "string-hash"; import hash from "string-hash";
import { PMTiles } from "pmtiles";
import {Map, LayerSpecification, StyleSpecification, ValidationError, SourceSpecification} from 'maplibre-gl' import {Map, LayerSpecification, StyleSpecification, ValidationError, SourceSpecification} from 'maplibre-gl'
import {latest, validateStyleMin} from '@maplibre/maplibre-gl-style-spec' import {latest, validateStyleMin} from '@maplibre/maplibre-gl-style-spec'
@@ -362,6 +363,7 @@ export default class App extends React.Component<any, AppState> {
[property]: value [property]: value
} }
} }
this.onStyleChanged(changedStyle) this.onStyleChanged(changedStyle)
} }
@@ -373,6 +375,24 @@ export default class App extends React.Component<any, AppState> {
...opts, ...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) { if (opts.initialLoad) {
this.getInitialStateFromUrl(newStyle); this.getInitialStateFromUrl(newStyle);
} }
@@ -641,33 +661,41 @@ export default class App extends React.Component<any, AppState> {
console.warn("Failed to setFetchAccessToken: ", err); console.warn("Failed to setFetchAccessToken: ", err);
} }
fetch(url!, { const setVectorLayers = (json:any) => {
mode: 'cors', if(!Object.prototype.hasOwnProperty.call(json, "vector_layers")) {
}) return;
.then(response => response.json()) }
.then(json => {
if(!Object.prototype.hasOwnProperty.call(json, "vector_layers")) { // Create new objects before setState
return; const sources = Object.assign({}, {
} [key]: this.state.sources[key],
// 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);
}); });
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 { else {
sourceList[key] = this.state.sources[key] || this.state.mapStyle.sources[key]; 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} onLayerSelect={this.onLayerSelect}
/> />
} else { } else {
mapElement = <MapMaplibreGl {...mapProps} mapElement = <MapMaplibreGl {...mapProps}
onChange={this.onMapChange} onChange={this.onMapChange}
options={this.state.maplibreGlDebugOptions} options={this.state.maplibreGlDebugOptions}
@@ -781,6 +810,7 @@ export default class App extends React.Component<any, AppState> {
getInitialStateFromUrl = (mapStyle: StyleSpecification) => { getInitialStateFromUrl = (mapStyle: StyleSpecification) => {
const url = new URL(location.href); const url = new URL(location.href);
const modalParam = url.searchParams.get("modal"); const modalParam = url.searchParams.get("modal");
if (modalParam && modalParam !== "") { if (modalParam && modalParam !== "") {
const modals = modalParam.split(","); const modals = modalParam.split(",");
const modalObj: {[key: string]: boolean} = {}; const modalObj: {[key: string]: boolean} = {};
-1
View File
@@ -102,4 +102,3 @@ export default class Block extends React.Component<BlockProps, BlockState> {
</label> </label>
} }
} }
-1
View File
@@ -31,4 +31,3 @@ export default class Collapse extends React.Component<CollapseProps> {
} }
} }
} }
-1
View File
@@ -16,4 +16,3 @@ export default class Collapser extends React.Component<CollapserProps> {
return this.props.isCollapsed ? <MdArrowDropUp style={iconStyle}/> : <MdArrowDropDown style={iconStyle} /> return this.props.isCollapsed ? <MdArrowDropUp style={iconStyle}/> : <MdArrowDropDown style={iconStyle} />
} }
} }
-1
View File
@@ -16,4 +16,3 @@ export default class FieldArray extends React.Component<FieldArrayProps> {
</Fieldset> </Fieldset>
} }
} }
-1
View File
@@ -15,4 +15,3 @@ export default class FieldAutocomplete extends React.Component<FieldAutocomplete
</Block> </Block>
} }
} }
-1
View File
@@ -15,4 +15,3 @@ export default class FieldCheckbox extends React.Component<FieldCheckboxProps> {
</Block> </Block>
} }
} }
-1
View File
@@ -18,4 +18,3 @@ export default class FieldColor extends React.Component<FieldColorProps> {
</Block> </Block>
} }
} }
-1
View File
@@ -13,4 +13,3 @@ export default class FieldDynamicArray extends React.Component<FieldDynamicArray
</Fieldset> </Fieldset>
} }
} }
+2 -2
View File
@@ -3,7 +3,7 @@ import InputEnum, {InputEnumProps} from './InputEnum'
import Fieldset from './Fieldset'; import Fieldset from './Fieldset';
type FieldEnumProps = InputEnumProps & { type FieldEnumProps = InputEnumProps & {
label?: string; label?: string;
fieldSpec?: { fieldSpec?: {
doc: string doc: string
@@ -17,4 +17,4 @@ export default class FieldEnum extends React.Component<FieldEnumProps> {
<InputEnum {...this.props} /> <InputEnum {...this.props} />
</Fieldset> </Fieldset>
} }
} }
-1
View File
@@ -404,4 +404,3 @@ export default class FieldFunction extends React.Component<FieldFunctionProps, F
</div> </div>
} }
} }
-1
View File
@@ -10,4 +10,3 @@ export default class FieldJson extends React.Component<FieldJsonProps> {
return <InputJson {...this.props} /> return <InputJson {...this.props} />
} }
} }
-1
View File
@@ -15,4 +15,3 @@ export default class FieldMultiInput extends React.Component<FieldMultiInputProp
</Fieldset> </Fieldset>
} }
} }
-2
View File
@@ -18,5 +18,3 @@ export default class FieldSelect extends React.Component<FieldSelectProps> {
</Block> </Block>
} }
} }
+2 -2
View File
@@ -22,8 +22,8 @@ class FieldSourceLayerInternal extends React.Component<FieldSourceLayerInternalP
render() { render() {
const t = this.props.t; const t = this.props.t;
return <Block return <Block
label={t("Source Layer")} label={t("Source Layer")}
fieldSpec={latest.layer['source-layer']} fieldSpec={latest.layer['source-layer']}
data-wd-key="layer-source-layer" data-wd-key="layer-source-layer"
error={this.props.error} error={this.props.error}
-1
View File
@@ -20,4 +20,3 @@ export default class FieldUrl extends React.Component<FieldUrlProps> {
); );
} }
} }
+2 -2
View File
@@ -255,8 +255,8 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
value={combiningOp} value={combiningOp}
onChange={(v: [string, any]) => this.onFilterPartChanged(0, v)} onChange={(v: [string, any]) => this.onFilterPartChanged(0, v)}
options={[ options={[
["all", t("every filter matches")], ["all", t("every filter matches")],
["none", t("no filter matches")], ["none", t("no filter matches")],
["any", t("any filter matches")] ["any", t("any filter matches")]
]} ]}
/> />
-2
View File
@@ -11,5 +11,3 @@ export default class IconBackground extends React.Component {
) )
} }
} }
-2
View File
@@ -11,5 +11,3 @@ export default class IconCircle extends React.Component {
) )
} }
} }
-2
View File
@@ -11,5 +11,3 @@ export default class IconFill extends React.Component {
) )
} }
} }
-1
View File
@@ -30,4 +30,3 @@ export default class IconLayer extends React.Component<IconLayerProps> {
} }
} }
} }
-2
View File
@@ -11,5 +11,3 @@ export default class IconLine extends React.Component {
) )
} }
} }
-3
View File
@@ -13,6 +13,3 @@ export default class IconSymbol extends React.Component {
) )
} }
} }
-1
View File
@@ -115,4 +115,3 @@ export default class FieldArray extends React.Component<FieldArrayProps, FieldAr
) )
} }
} }
-2
View File
@@ -98,5 +98,3 @@ export default class InputAutocomplete extends React.Component<InputAutocomplete
</div> </div>
} }
} }
+1 -1
View File
@@ -30,4 +30,4 @@ export default class InputButton extends React.Component<InputButtonProps> {
{this.props.children} {this.props.children}
</button> </button>
} }
} }
+1 -1
View File
@@ -34,4 +34,4 @@ export default class InputCheckbox extends React.Component<InputCheckboxProps> {
</div> </div>
</div> </div>
} }
} }
-1
View File
@@ -133,4 +133,3 @@ export default class InputColor extends React.Component<InputColorProps> {
</div> </div>
} }
} }
+2 -3
View File
@@ -22,7 +22,7 @@ export type FieldDynamicArrayProps = {
} }
'aria-label'?: string 'aria-label'?: string
label: string label: string
} }
type FieldDynamicArrayInternalProps = FieldDynamicArrayProps & WithTranslation; type FieldDynamicArrayInternalProps = FieldDynamicArrayProps & WithTranslation;
@@ -67,7 +67,7 @@ class FieldDynamicArrayInternal extends React.Component<FieldDynamicArrayInterna
const t = this.props.t; const t = this.props.t;
const i18nProps = { t, i18n: this.props.i18n, tReady: this.props.tReady }; const i18nProps = { t, i18n: this.props.i18n, tReady: this.props.tReady };
const inputs = this.values.map((v, i) => { const inputs = this.values.map((v, i) => {
const deleteValueBtn= <DeleteValueInputButton const deleteValueBtn= <DeleteValueInputButton
onClick={this.deleteValue.bind(this, i)} onClick={this.deleteValue.bind(this, i)}
{...i18nProps} {...i18nProps}
/>; />;
@@ -152,4 +152,3 @@ class DeleteValueInputButton extends React.Component<DeleteValueInputButtonProps
</InputButton> </InputButton>
} }
} }
-1
View File
@@ -47,4 +47,3 @@ export default class InputEnum extends React.Component<InputEnumProps> {
} }
} }
} }
-2
View File
@@ -37,5 +37,3 @@ export default class InputMultiInput extends React.Component<InputMultiInputProp
</fieldset> </fieldset>
} }
} }
+1 -3
View File
@@ -157,7 +157,7 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
render() { render() {
if( if(
Object.prototype.hasOwnProperty.call(this.props, "min") && Object.prototype.hasOwnProperty.call(this.props, "min") &&
Object.prototype.hasOwnProperty.call(this.props, "max") && 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
@@ -244,5 +244,3 @@ export default class InputNumber extends React.Component<InputNumberProps, Input
} }
} }
} }
-2
View File
@@ -30,5 +30,3 @@ export default class InputSelect extends React.Component<InputSelectProps> {
</select> </select>
} }
} }
-2
View File
@@ -97,5 +97,3 @@ export default class InputString extends React.Component<InputStringProps, Input
}); });
} }
} }
+1 -1
View File
@@ -15,7 +15,7 @@ function validate(url: string, t: TFunction): JSX.Element | undefined {
const urlObj = new URL(url); const urlObj = new URL(url);
return urlObj.protocol; return urlObj.protocol;
} }
catch (err) { catch (_err) {
return undefined; return undefined;
} }
}; };
+4 -4
View File
@@ -326,10 +326,10 @@ class LayerEditorInternal extends React.Component<LayerEditorInternalProps, Laye
onSelection={handleSelection} onSelection={handleSelection}
closeOnSelection={false} closeOnSelection={false}
> >
<Button <Button
id="skip-target-layer-editor" id="skip-target-layer-editor"
data-wd-key="skip-target-layer-editor" data-wd-key="skip-target-layer-editor"
className='more-menu__button' className='more-menu__button'
title={"Layer options"}> title={"Layer options"}>
<MdMoreVert className="more-menu__button__svg" /> <MdMoreVert className="more-menu__button__svg" />
</Button> </Button>
+3 -3
View File
@@ -287,9 +287,9 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
data-wd-key="skip-target-layer-list" data-wd-key="skip-target-layer-list"
onClick={this.toggleLayers} onClick={this.toggleLayers}
className="maputnik-button"> className="maputnik-button">
{this.state.areAllGroupsExpanded === true ? {this.state.areAllGroupsExpanded === true ?
t("Collapse") t("Collapse")
: :
t("Expand") t("Expand")
} }
</button> </button>
+6 -3
View File
@@ -15,6 +15,7 @@ import MaplibreGeocoder, { MaplibreGeocoderApi, MaplibreGeocoderApiConfig } from
import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css'; import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css';
import { withTranslation, WithTranslation } from 'react-i18next' import { withTranslation, WithTranslation } from 'react-i18next'
import i18next from 'i18next' import i18next from 'i18next'
import { Protocol } from "pmtiles";
function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container): HTMLElement { function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container): HTMLElement {
ReactDOM.render(popup, mountNode); ReactDOM.render(popup, mountNode);
@@ -103,7 +104,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
let should = false; let should = false;
try { try {
should = JSON.stringify(this.props) !== JSON.stringify(nextProps) || JSON.stringify(this.state) !== JSON.stringify(nextState); should = JSON.stringify(this.props) !== JSON.stringify(nextProps) || JSON.stringify(this.state) !== JSON.stringify(nextState);
} catch(e) { } catch(_e) {
// no biggie, carry on // no biggie, carry on
} }
return should; return should;
@@ -133,7 +134,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
this.state.inspect!.render(); this.state.inspect!.render();
}, 500); }, 500);
} }
} }
componentDidMount() { componentDidMount() {
@@ -148,6 +149,8 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
localIdeographFontFamily: false localIdeographFontFamily: false
} satisfies MapOptions; } satisfies MapOptions;
const protocol = new Protocol({metadata: true});
MapLibreGl.addProtocol("pmtiles",protocol.tile);
const map = new MapLibreGl.Map(mapOpts); const map = new MapLibreGl.Map(mapOpts);
const mapViewChange = () => { const mapViewChange = () => {
@@ -161,7 +164,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
map.showCollisionBoxes = mapOpts.showCollisionBoxes!; map.showCollisionBoxes = mapOpts.showCollisionBoxes!;
map.showOverdrawInspector = mapOpts.showOverdrawInspector!; map.showOverdrawInspector = mapOpts.showOverdrawInspector!;
let geocoder = this.initGeocoder(map); const geocoder = this.initGeocoder(map);
const zoomControl = new ZoomControl(); const zoomControl = new ZoomControl();
map.addControl(zoomControl, 'top-right'); map.addControl(zoomControl, 'top-right');
+1 -1
View File
@@ -27,7 +27,7 @@ class ModalLoadingInternal extends React.Component<ModalLoadingInternalProps> {
underlayClickExits={false} underlayClickExits={false}
underlayProps={{ underlayProps={{
// @ts-ignore // @ts-ignore
onClick: (e: Event) => underlayProps(e) onClick: (e: Event) => underlayProps(e)
}} }}
title={this.props.title} title={this.props.title}
onOpenToggle={() => this.props.onCancel()} onOpenToggle={() => this.props.onCancel()}
+6
View File
@@ -51,6 +51,7 @@ function editorMode(source: SourceSpecification) {
} }
if(source.type === 'vector') { if(source.type === 'vector') {
if(source.tiles) return 'tile_vector' if(source.tiles) return 'tile_vector'
if(source.url && source.url.startsWith("pmtiles://")) return 'pmtiles_vector'
return 'tilejson_vector' return 'tilejson_vector'
} }
if(source.type === 'geojson') { if(source.type === 'geojson') {
@@ -129,6 +130,10 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
const {protocol} = window.location; const {protocol} = window.location;
switch(mode) { switch(mode) {
case 'pmtiles_vector': return {
type: 'vector',
url: `${protocol}//localhost:3000/file.pmtiles`
}
case 'geojson_url': return { case 'geojson_url': return {
type: 'geojson', type: 'geojson',
data: `${protocol}//localhost:3000/geojson.json` data: `${protocol}//localhost:3000/geojson.json`
@@ -240,6 +245,7 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
['tile_raster', t('Raster (Tile URLs)')], ['tile_raster', t('Raster (Tile URLs)')],
['tilejson_raster-dem', t('Raster DEM (TileJSON URL)')], ['tilejson_raster-dem', t('Raster DEM (TileJSON URL)')],
['tilexyz_raster-dem', t('Raster DEM (XYZ URLs)')], ['tilexyz_raster-dem', t('Raster DEM (XYZ URLs)')],
['pmtiles_vector', t('Vector (PMTiles)')],
['image', t('Image')], ['image', t('Image')],
['video', t('Video')], ['video', t('Video')],
]} ]}
+29 -1
View File
@@ -11,7 +11,7 @@ import FieldCheckbox from './FieldCheckbox'
import { WithTranslation, withTranslation } from 'react-i18next'; import { WithTranslation, withTranslation } from 'react-i18next';
import { TFunction } from '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 = { type TileJSONSourceEditorProps = {
source: { 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 = { type ModalSourcesTypeEditorInternalProps = {
mode: EditorMode mode: EditorMode
source: any source: any
@@ -343,6 +370,7 @@ class ModalSourcesTypeEditorInternal extends React.Component<ModalSourcesTypeEdi
value={this.props.source.encoding || latest.source_raster_dem.encoding.default} value={this.props.source.encoding || latest.source_raster_dem.encoding.default}
/> />
</TileURLSourceEditor> </TileURLSourceEditor>
case 'pmtiles_vector': return <PMTilesSourceEditor {...commonProps} />
case 'image': return <ImageSourceEditor {...commonProps} /> case 'image': return <ImageSourceEditor {...commonProps} />
case 'video': return <VideoSourceEditor {...commonProps} /> case 'video': return <VideoSourceEditor {...commonProps} />
default: return null default: return null
+2 -2
View File
@@ -56,8 +56,8 @@ export default class PropertyGroup extends React.Component<PropertyGroupProps> {
const paint = this.props.layer.paint || {} const paint = this.props.layer.paint || {}
const layout = this.props.layer.layout || {} const layout = this.props.layer.layout || {}
const fieldValue = fieldName in paint const fieldValue = fieldName in paint
? paint[fieldName as keyof typeof paint] ? paint[fieldName as keyof typeof paint]
: layout[fieldName as keyof typeof layout] : layout[fieldName as keyof typeof layout]
const fieldType = fieldName in paint ? 'paint' : 'layout'; const fieldType = fieldName in paint ? 'paint' : 'layout';
-1
View File
@@ -11,4 +11,3 @@ export default class ScrollContainer extends React.Component<ScrollContainerProp
</div> </div>
} }
} }
-1
View File
@@ -90,4 +90,3 @@ export default class SingleFilterEditor extends React.Component<SingleFilterEdit
</div> </div>
} }
} }
+3 -3
View File
@@ -1,7 +1,7 @@
@import '../styles/vars'; @use '../styles/vars';
.SmallError { .SmallError {
color: #E57373; color: #E57373;
font-size: $font-size-6; font-size: vars.$font-size-6;
margin-top: $margin-2 margin-top: vars.$margin-2
} }
-1
View File
@@ -44,4 +44,3 @@ export default class SpecField extends React.Component<SpecFieldProps> {
</TypeBlock> </TypeBlock>
} }
} }
+1 -1
View File
@@ -91,7 +91,7 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
function getValue(data: any) { function getValue(data: any) {
return stringifyPretty(data, {indent: 2, maxLength: 38}) return stringifyPretty(data, {indent: 2, maxLength: 38})
} }
if (jsonError) { if (jsonError) {
foundErrors.push({message: "Invalid JSON"}); foundErrors.push({message: "Invalid JSON"});
} }
+2 -2
View File
@@ -29,8 +29,8 @@ export default class SpecProperty extends React.Component<SpecPropertyProps> {
const functionBtn = <FunctionButtons const functionBtn = <FunctionButtons
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
onZoomClick={this.props.onZoomClick} onZoomClick={this.props.onZoomClick}
onDataClick={this.props.onDataClick} onDataClick={this.props.onDataClick}
onExpressionClick={this.props.onExpressionClick} onExpressionClick={this.props.onExpressionClick}
/> />
const error = errors![fieldType+"."+fieldName as any] as any; const error = errors![fieldType+"."+fieldName as any] as any;
+1
View File
@@ -8,6 +8,7 @@ export const supportedLanguages = {
"en": "English", "en": "English",
"fr": "Français", "fr": "Français",
"he": "עברית", "he": "עברית",
"it": "Italiano",
"ja": "日本語", "ja": "日本語",
"zh": "简体中文" "zh": "简体中文"
} as const; } as const;
+31 -36
View File
@@ -1,5 +1,4 @@
// @ts-ignore - this is a fork of jsonlint import {parse} from '@prantlf/jsonlint';
import jsonlint from 'jsonlint';
import CodeMirror, { MarkerRange } from 'codemirror'; import CodeMirror, { MarkerRange } from 'codemirror';
import jsonToAst from 'json-to-ast'; import jsonToAst from 'json-to-ast';
import {expression, validateStyleMin} from '@maplibre/maplibre-gl-style-spec'; 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[] = []; 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 { try {
jsonlint.parse(text); parse(text);
} }
catch(e) { catch(err: any) {
// Do nothing we catch the error above
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; return found;
}
CodeMirror.registerHelper("lint", "json", (text: string) => {
return tryToParse(text);
}); });
CodeMirror.registerHelper("lint", "mgl", (text: string, opts: any, doc: any) => { 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 found: MarkerRangeWithMessage[] = tryToParse(text);
const loc = hash.loc;
found.push({ const {context} = opts;
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
}
if (found.length > 0) { if (found.length > 0) {
// JSON invalid so don't go any further // JSON invalid so don't go any further
+1 -1
View File
@@ -11,4 +11,4 @@ export function undoMessages(beforeStyle: StyleSpecification, afterStyle: StyleS
} }
export function redoMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) { export function redoMessages(beforeStyle: StyleSpecification, afterStyle: StyleSpecification) {
return diffMessages(beforeStyle, afterStyle).map(m => 'Redo ' + m) return diffMessages(beforeStyle, afterStyle).map(m => 'Redo ' + m)
} }
+7 -4
View File
@@ -5,14 +5,17 @@ function loadJSON(url: string, defaultValue: any, cb: (...args: any[]) => void)
mode: 'cors', mode: 'cors',
credentials: "same-origin" credentials: "same-origin"
}) })
.then(function(response) { .then((response) => {
if (!response.ok) {
throw new Error('Failed to load metadata for ' + url);
}
return response.json(); return response.json();
}) })
.then(function(body) { .then((body) => {
cb(body) cb(body)
}) })
.catch(function() { .catch(() => {
console.warn('Can not metadata for ' + url) console.warn('Can not load metadata for ' + url + ', using default value ' + defaultValue);
cb(defaultValue) cb(defaultValue)
}) })
} }
-1
View File
@@ -24,4 +24,3 @@ export function changeSource(mapStyle: StyleSpecification, sourceId: string, sou
sources: changedSources sources: changedSources
} }
} }
-1
View File
@@ -1,4 +1,3 @@
// @ts-ignore
import style from './style' import style from './style'
export function initialStyleUrl() { export function initialStyleUrl() {
+3 -3
View File
@@ -14,10 +14,10 @@ export default class ZoomControl {
this._container.setAttribute("data-wd-key", "maplibre:ctrl-zoom"); this._container.setAttribute("data-wd-key", "maplibre:ctrl-zoom");
this.setLabel("Zoom:"); this.setLabel("Zoom:");
this.addEventListeners(); this.addEventListeners();
return this._container; return this._container;
} }
updateZoomLevel() { updateZoomLevel() {
this._textEl!.innerHTML = this._map!.getZoom().toFixed(2); this._textEl!.innerHTML = this._map!.getZoom().toFixed(2);
} }
@@ -29,7 +29,7 @@ export default class ZoomControl {
this._textEl = this._container!.querySelector("span"); this._textEl = this._container!.querySelector("span");
this.updateZoomLevel(); this.updateZoomLevel();
} }
addEventListeners (){ addEventListeners (){
this._map!.on('render', () => this.updateZoomLevel()); this._map!.on('render', () => this.updateZoomLevel());
this._map!.on('zoomIn', () => this.updateZoomLevel()); this._map!.on('zoomIn', () => this.updateZoomLevel());
+7 -8
View File
@@ -46,15 +46,14 @@ The following users can help you with the relevant languages:
| ISO Code | Language | User | | ISO Code | Language | User |
|----------|--------------------|--------------------------------------------| |----------|--------------------|--------------------------------------------|
| de | German | [@josxha](https://github.com/josxha) | | de | German | [@josxha](https://github.com/josxha) |
| en | English | [@HarelM](https://github.com/HarelM) | | en | English | [@HarelM](https://github.com/HarelM) |
| fr | French | [@lhapaipai](https://github.com/lhapaipai) | | fr | French | [@lhapaipai](https://github.com/lhapaipai) |
| hr | Hebrew | [@HarelM](https://github.com/HarelM) | | hr | Hebrew | [@HarelM](https://github.com/HarelM) |
| ja | Japanese | [@keichan34](https://github.com/keichan34) | | it | Italian | [@napo](https://github.com/napo) |
| zh | Simplified Chinese | [@jieme](https://github.com/jieme) | | 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 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. 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. You can use incognito mode to check a first time usage.
+2
View File
@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "Raster (Tile URLs)", "Raster (Tile URLs)": "Raster (Tile URLs)",
"Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)", "Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)",
"Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)", "Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)",
"Vector (PMTiles)": "Vektor (PMTiles)",
"Image": "Bild", "Image": "Bild",
"Video": "Video", "Video": "Video",
"Add Source": "Quelle hinzufügen", "Add Source": "Quelle hinzufügen",
@@ -170,6 +171,7 @@
"GeoJSON URL": "GeoJSON URL", "GeoJSON URL": "GeoJSON URL",
"GeoJSON": "GeoJSON", "GeoJSON": "GeoJSON",
"Cluster": "Cluster", "Cluster": "Cluster",
"PMTiles URL": "PMTiles URL",
"Tile Size": "Kachelgröße", "Tile Size": "Kachelgröße",
"Encoding": "Kodierung", "Encoding": "Kodierung",
"Error:": "Fehler:", "Error:": "Fehler:",
+2
View File
@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "Raster (URLs Tile)", "Raster (Tile URLs)": "Raster (URLs Tile)",
"Raster DEM (TileJSON URL)": "Raster DEM (URL TileJSON)", "Raster DEM (TileJSON URL)": "Raster DEM (URL TileJSON)",
"Raster DEM (XYZ URLs)": "Raster DEM (URLs XYZ)", "Raster DEM (XYZ URLs)": "Raster DEM (URLs XYZ)",
"Vector (PMTiles)": "Vecteur (PMTiles)",
"Image": "Image", "Image": "Image",
"Video": "Vidéo", "Video": "Vidéo",
"Add Source": "Ajouter une source", "Add Source": "Ajouter une source",
@@ -170,6 +171,7 @@
"GeoJSON URL": "URL GeoJSON", "GeoJSON URL": "URL GeoJSON",
"GeoJSON": "GeoJSON", "GeoJSON": "GeoJSON",
"Cluster": "Cluster", "Cluster": "Cluster",
"PMTiles URL": "URL PMTiles",
"Tile Size": "Dimension d'une tuile", "Tile Size": "Dimension d'une tuile",
"Encoding": "Encodage", "Encoding": "Encodage",
"Error:": "Erreur :", "Error:": "Erreur :",
+2
View File
@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "Raster (Tile URLs)", "Raster (Tile URLs)": "Raster (Tile URLs)",
"Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)", "Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)",
"Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)", "Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)",
"Vector (PMTiles)": "Vector (PMTiles)",
"Image": "תמונה", "Image": "תמונה",
"Video": "וידאו", "Video": "וידאו",
"Add Source": "הוספת מקור", "Add Source": "הוספת מקור",
@@ -170,6 +171,7 @@
"GeoJSON URL": "כתובת GeoJSON", "GeoJSON URL": "כתובת GeoJSON",
"GeoJSON": "GeoJSON", "GeoJSON": "GeoJSON",
"Cluster": "קיבוץ", "Cluster": "קיבוץ",
"PMTiles URL": "כתובת PMTiles",
"Tile Size": "גודל אריח", "Tile Size": "גודל אריח",
"Encoding": "קידוד", "Encoding": "קידוד",
"Error:": "שגיאה", "Error:": "שגיאה",
+189
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."
}
+2
View File
@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "ラスタ (Tile URLs)", "Raster (Tile URLs)": "ラスタ (Tile URLs)",
"Raster DEM (TileJSON URL)": "ラスタ DEM (TileJSON URL)", "Raster DEM (TileJSON URL)": "ラスタ DEM (TileJSON URL)",
"Raster DEM (XYZ URLs)": "ラスタ DEM (XYZ URL)", "Raster DEM (XYZ URLs)": "ラスタ DEM (XYZ URL)",
"Vector (PMTiles)": "ベクトル (PMTiles)",
"Image": "画像", "Image": "画像",
"Video": "動画", "Video": "動画",
"Add Source": "ソースを追加", "Add Source": "ソースを追加",
@@ -170,6 +171,7 @@
"GeoJSON URL": "GeoJSON URL", "GeoJSON URL": "GeoJSON URL",
"GeoJSON": "GeoJSON", "GeoJSON": "GeoJSON",
"Cluster": "クラスタ", "Cluster": "クラスタ",
"PMTiles URL": "PMTiles URL",
"Tile Size": "タイルサイズ", "Tile Size": "タイルサイズ",
"Encoding": "エンコーディング", "Encoding": "エンコーディング",
"Error:": "エラー:", "Error:": "エラー:",
+2
View File
@@ -149,6 +149,7 @@
"Raster (Tile URLs)": "栅格数据 (Tile URLs)", "Raster (Tile URLs)": "栅格数据 (Tile URLs)",
"Raster DEM (TileJSON URL)": "栅格高程数据 (TileJSON URL)", "Raster DEM (TileJSON URL)": "栅格高程数据 (TileJSON URL)",
"Raster DEM (XYZ URLs)": "栅格高程数据 (XYZ URLs)", "Raster DEM (XYZ URLs)": "栅格高程数据 (XYZ URLs)",
"Vector (PMTiles)": "__STRING_NOT_TRANSLATED__",
"Image": "图像", "Image": "图像",
"Video": "视频", "Video": "视频",
"Add Source": "添加源", "Add Source": "添加源",
@@ -170,6 +171,7 @@
"GeoJSON URL": "GeoJSON URL", "GeoJSON URL": "GeoJSON URL",
"GeoJSON": "GeoJSON", "GeoJSON": "GeoJSON",
"Cluster": "聚合", "Cluster": "聚合",
"PMTiles URL": "__STRING_NOT_TRANSLATED__",
"Tile Size": "__STRING_NOT_TRANSLATED__", "Tile Size": "__STRING_NOT_TRANSLATED__",
"Encoding": "编码", "Encoding": "编码",
"Error:": "错误:", "Error:": "错误:",
-1
View File
@@ -88,4 +88,3 @@
.maplibregl-ctrl-map { .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'); 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');
} }
+18 -16
View File
@@ -1,3 +1,5 @@
@use "vars";
@font-face { @font-face {
font-family: 'Roboto'; font-family: 'Roboto';
src: url('../fonts/Roboto-Regular.ttf') format('truetype'); src: url('../fonts/Roboto-Regular.ttf') format('truetype');
@@ -15,8 +17,8 @@
} }
html { html {
color: $color-white; color: vars.$color-white;
font-size: $font-size-5; font-size: vars.$font-size-5;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -32,34 +34,34 @@ body {
} }
p { p {
font-size: $font-size-6; font-size: vars.$font-size-6;
margin-top: $margin-2; margin-top: vars.$margin-2;
margin-bottom: $margin-2; margin-bottom: vars.$margin-2;
color: $color-lowgray; color: vars.$color-lowgray;
line-height: 1.3; line-height: 1.3;
} }
h1 { h1 {
font-size: $font-size-2; font-size: vars.$font-size-2;
margin-bottom: $margin-3; margin-bottom: vars.$margin-3;
font-weight: bold; font-weight: bold;
} }
h2 { h2 {
font-size: $font-size-3; font-size: vars.$font-size-3;
margin-bottom: $margin-3; margin-bottom: vars.$margin-3;
font-weight: bold; font-weight: bold;
} }
h3 { h3 {
font-size: $font-size-4; font-size: vars.$font-size-4;
margin-bottom: $margin-3; margin-bottom: vars.$margin-3;
font-weight: bold; font-weight: bold;
} }
h4 { h4 {
font-size: $font-size-5; font-size: vars.$font-size-5;
margin-bottom: $margin-3; margin-bottom: vars.$margin-3;
} }
input:focus, input:focus,
@@ -68,12 +70,12 @@ textarea:focus,
button:focus, button:focus,
.maputnik-toolbar-link:focus, .maputnik-toolbar-link:focus,
select:focus { select:focus {
color: $color-white; color: vars.$color-white;
outline: #8e8e8e auto 1px; outline: #8e8e8e auto 1px;
} }
label:hover { label:hover {
color: $color-white; color: vars.$color-white;
} }
.clearfix { .clearfix {
+5 -3
View File
@@ -1,3 +1,5 @@
@use "vars";
.CodeMirror-lint-tooltip { .CodeMirror-lint-tooltip {
z-index: 2000 !important; z-index: 2000 !important;
} }
@@ -55,7 +57,7 @@
.cm-s-maputnik .CodeMirror-matchingbracket { .cm-s-maputnik .CodeMirror-matchingbracket {
background: hsla(223, 12%, 35%, 1); background: hsla(223, 12%, 35%, 1);
color: $color-white !important; color: vars.$color-white !important;
} }
.cm-s-maputnik .CodeMirror-nonmatchingbracket { .cm-s-maputnik .CodeMirror-nonmatchingbracket {
@@ -69,7 +71,7 @@
} }
to { to {
opacity: 0; opacity: 0;
} }
} }
.JSONEditor__message { .JSONEditor__message {
@@ -79,7 +81,7 @@
z-index: 99999; z-index: 99999;
padding: 0.3em 0.5em; padding: 0.3em 0.5em;
background: hsla(0, 0%, 0%, 0.3); background: hsla(0, 0%, 0%, 0.3);
color: $color-lowgray; color: vars.$color-lowgray;
border-bottom-left-radius: 2px; border-bottom-left-radius: 2px;
transition: opacity 320ms ease; transition: opacity 320ms ease;
opacity: 0; opacity: 0;
+45 -43
View File
@@ -1,9 +1,12 @@
@use "mixins";
@use "vars";
@use 'sass:color'; @use 'sass:color';
// MAP // MAP
.maputnik-map__container { .maputnik-map__container {
background: white; background: white;
display: flex; display: flex;
width: $layout-map-width; width: vars.$layout-map-width;
&--error { &--error {
align-items: center; align-items: center;
@@ -36,7 +39,7 @@
&-wrapper { &-wrapper {
display: inline-block; display: inline-block;
box-sizing: border-box; box-sizing: border-box;
font-size: $font-size-6; font-size: vars.$font-size-6;
line-height: 2; line-height: 2;
user-select: none; user-select: none;
position: relative; position: relative;
@@ -45,9 +48,9 @@
&-popup { &-popup {
display: none; display: none;
color: $color-lowgray; color: vars.$color-lowgray;
background-color: $color-gray; background-color: vars.$color-gray;
padding: $margin-2; padding: vars.$margin-2;
font-size: 10px; font-size: 10px;
position: absolute; position: absolute;
top: 20px; top: 20px;
@@ -60,7 +63,7 @@
&-button { &-button {
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
background: $color-black; background: vars.$color-black;
color: white; color: white;
border: none; border: none;
padding: 4px; padding: 4px;
@@ -77,11 +80,11 @@
} }
.maputnik-doc-inline { .maputnik-doc-inline {
color: $color-lowgray; color: vars.$color-lowgray;
background-color: $color-gray; background-color: vars.$color-gray;
padding: $margin-2; padding: vars.$margin-2;
font-size: 12px; font-size: 12px;
margin-top: $margin-3; margin-top: vars.$margin-3;
line-height: 1.5; line-height: 1.5;
flex: 1 0; flex: 1 0;
} }
@@ -95,10 +98,10 @@
.maputnik-button { .maputnik-button {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
background-color: $color-midgray; background-color: vars.$color-midgray;
color: $color-lowgray; color: vars.$color-lowgray;
font-size: $font-size-6; font-size: vars.$font-size-6;
padding: $margin-2; padding: vars.$margin-2;
user-select: none; user-select: none;
border-width: 0; border-width: 0;
border-radius: 2px; border-radius: 2px;
@@ -106,36 +109,36 @@
text-decoration: none; text-decoration: none;
&:hover { &:hover {
background-color: color.adjust($color-midgray, $lightness: 12%); background-color: color.adjust(vars.$color-midgray, $lightness: 12%);
color: $color-white; color: vars.$color-white;
} }
&:disabled { &:disabled {
background-color: color.adjust($color-midgray, $lightness: -5%); background-color: color.adjust(vars.$color-midgray, $lightness: -5%);
color: $color-midgray; color: vars.$color-midgray;
cursor: not-allowed; cursor: not-allowed;
} }
} }
.maputnik-big-button { .maputnik-big-button {
margin-top: $margin-3; margin-top: vars.$margin-3;
display: inline-block; display: inline-block;
padding: $margin-3; padding: vars.$margin-3;
font-size: $font-size-5; font-size: vars.$font-size-5;
} }
.maputnik-wide-button { .maputnik-wide-button {
padding: $margin-2 $margin-3; padding: vars.$margin-2 vars.$margin-3;
} }
.maputnik-green-button { .maputnik-green-button {
background-color: $color-green; background-color: vars.$color-green;
color: $color-black; color: vars.$color-black;
} }
.maputnik-white-button { .maputnik-white-button {
background-color: $color-white; background-color: vars.$color-white;
color: $color-black; color: vars.$color-black;
} }
.maputnik-icon-button { .maputnik-icon-button {
@@ -150,19 +153,19 @@
} }
svg { svg {
fill: $color-white; fill: vars.$color-white;
} }
} }
} }
// INPUT BLOCK // INPUT BLOCK
.maputnik-input-block { .maputnik-input-block {
margin: $margin-3; margin: vars.$margin-3;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
&-label { &-label {
color: $color-lowgray; color: vars.$color-lowgray;
user-select: none; user-select: none;
width: 32%; width: 32%;
vertical-align: top; vertical-align: top;
@@ -170,7 +173,7 @@
} }
&-action { &-action {
color: $color-lowgray; color: vars.$color-lowgray;
user-select: none; user-select: none;
width: 18%; width: 18%;
vertical-align: top; vertical-align: top;
@@ -187,7 +190,7 @@
.maputnik-input-block-label { .maputnik-input-block-label {
display: inline-block; display: inline-block;
width: 32%; width: 32%;
margin-bottom: $margin-3; margin-bottom: vars.$margin-3;
} }
.maputnik-input-block-action { .maputnik-input-block-action {
@@ -211,15 +214,15 @@
// SPACE HELPER // SPACE HELPER
.maputnik-space { .maputnik-space {
@include vendor-prefix(flex-grow, 1); @include mixins.vendor-prefix(flex-grow, 1);
} }
// MESSAGE PANEL // MESSAGE PANEL
.maputnik-message-panel { .maputnik-message-panel {
padding: $margin-2; padding: vars.$margin-2;
&-error { &-error {
color: $color-red; color: vars.$color-red;
} }
&__switch-button { &__switch-button {
@@ -241,7 +244,7 @@
&__menu { &__menu {
position: absolute; position: absolute;
z-index: 999999; z-index: 999999;
background: $color-black; background: vars.$color-black;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-content: stretch; align-content: stretch;
@@ -262,13 +265,13 @@
color: #a4a4a4; color: #a4a4a4;
padding: 0.4em 0.4em; padding: 0.4em 0.4em;
font-size: 0.9em; font-size: 0.9em;
border: solid 1px $color-red; border: solid 1px vars.$color-red;
border-radius: 2px; border-radius: 2px;
margin: $margin-2 0px; margin: vars.$margin-2 0px;
} }
.maputnik-expression-editor { .maputnik-expression-editor {
border: solid 1px $color-gray; border: solid 1px vars.$color-gray;
} }
.maputnik-input-block--wide { .maputnik-input-block--wide {
@@ -288,13 +291,13 @@
} }
.maputnik-expr-infobox { .maputnik-expr-infobox {
font-size: $font-size-6; font-size: vars.$font-size-6;
background: $color-midgray; background: vars.$color-midgray;
padding: $margin-2; padding: vars.$margin-2;
border-radius: 2px; border-radius: 2px;
border-top-right-radius: 0px; border-top-right-radius: 0px;
border-top-left-radius: 0px; border-top-left-radius: 0px;
color: $color-white; color: vars.$color-white;
} }
.maputnik-expr-infobox__button { .maputnik-expr-infobox__button {
@@ -305,4 +308,3 @@
color: currentColor; color: currentColor;
cursor: pointer; cursor: pointer;
} }
+13 -11
View File
@@ -1,5 +1,7 @@
@use "vars";
.maputnik-filter-editor-wrapper { .maputnik-filter-editor-wrapper {
padding: $margin-3; padding: vars.$margin-3;
overflow: hidden; overflow: hidden;
.maputnik-input-block { .maputnik-input-block {
@@ -8,8 +10,8 @@
} }
.maputnik-filter-editor { .maputnik-filter-editor {
@extend .clearfix; /* stylelint-disable-line */ @extend .clearfix !optional; /* stylelint-disable-line */
color: $color-lowgray; color: vars.$color-lowgray;
} }
.maputnik-filter-editor-property { .maputnik-filter-editor-property {
@@ -39,7 +41,7 @@
} }
.maputnik-filter-editor-compound-select { .maputnik-filter-editor-compound-select {
margin-bottom: $margin-2; margin-bottom: vars.$margin-2;
.maputnik-doc-wrapper { .maputnik-doc-wrapper {
width: 50%; width: 50%;
@@ -52,22 +54,22 @@
} }
.maputnik-filter-editor-unsupported { .maputnik-filter-editor-unsupported {
color: $color-midgray; color: vars.$color-midgray;
} }
.maputnik-add-filter { .maputnik-add-filter {
display: inline-block; display: inline-block;
float: right; float: right;
margin-top: $margin-3; margin-top: vars.$margin-3;
} }
.maputnik-delete-filter { .maputnik-delete-filter {
@extend .maputnik-icon-button; /* stylelint-disable-line */ @extend .maputnik-icon-button !optional; /* stylelint-disable-line */
} }
.maputnik-filter-editor-block-action { .maputnik-filter-editor-block-action {
margin-top: $margin-2; margin-top: vars.$margin-2;
margin-bottom: $margin-2; margin-bottom: vars.$margin-2;
display: inline-block; display: inline-block;
width: 6%; width: 6%;
margin-right: 1.5%; margin-right: 1.5%;
@@ -79,12 +81,12 @@
} }
.maputnik-radio-as-button { .maputnik-radio-as-button {
@extend .maputnik-button; /* stylelint-disable-line */ @extend .maputnik-button !optional; /* stylelint-disable-line */
border: solid 1px transparent; border: solid 1px transparent;
&:focus-within { &:focus-within {
border: solid 1px $color-white; border: solid 1px vars.$color-white;
} }
input { input {
+35 -32
View File
@@ -1,3 +1,6 @@
@use "mixins";
@use "vars";
@use 'sass:color'; @use 'sass:color';
//INPUT //INPUT
.maputnik-input { .maputnik-input {
@@ -5,13 +8,13 @@
width: 100%; width: 100%;
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
font-size: $font-size-6; font-size: vars.$font-size-6;
line-height: 2; line-height: 2;
padding-left: $margin-2; padding-left: vars.$margin-2;
padding-right: $margin-2; padding-right: vars.$margin-2;
border: none; border: none;
background-color: $color-gray; background-color: vars.$color-gray;
color: color.adjust($color-lowgray, $lightness: 12%); color: color.adjust(vars.$color-lowgray, $lightness: 12%);
&:invalid { &:invalid {
border: solid 1px #B71C1C; border: solid 1px #B71C1C;
@@ -20,7 +23,7 @@
} }
.maputnik-string { .maputnik-string {
@extend .maputnik-input; /* stylelint-disable-line */ @extend .maputnik-input !optional; /* stylelint-disable-line */
&--multi { &--multi {
resize: vertical; resize: vertical;
@@ -44,12 +47,12 @@
} }
.maputnik-number { .maputnik-number {
@extend .maputnik-input; /* stylelint-disable-line */ @extend .maputnik-input !optional; /* stylelint-disable-line */
} }
//COLOR PICKER //COLOR PICKER
.maputnik-color { .maputnik-color {
@extend .maputnik-input; /* stylelint-disable-line */ @extend .maputnik-input !optional; /* stylelint-disable-line */
height: 26px; height: 26px;
} }
@@ -57,7 +60,7 @@
.maputnik-color-wrapper { .maputnik-color-wrapper {
position: relative; position: relative;
@include flex-row; @include mixins.flex-row;
} }
.maputnik-color-swatch { .maputnik-color-swatch {
@@ -71,7 +74,7 @@
.maputnik-array { .maputnik-array {
> * { > * {
margin-bottom: $margin-3; margin-bottom: vars.$margin-3;
} }
.maputnik-array-block { .maputnik-array-block {
@@ -96,19 +99,19 @@
// SELECT // SELECT
.maputnik-select { .maputnik-select {
@extend .maputnik-input; /* stylelint-disable-line */ @extend .maputnik-input !optional; /* stylelint-disable-line */
-moz-appearance: none; -moz-appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
background: $color-gray url("#{$icon-down-arrow}") right center no-repeat; background: vars.$color-gray url("#{vars.$icon-down-arrow}") right center no-repeat;
color: $color-white; color: vars.$color-white;
background-position: calc(100% - 2px) center; background-position: calc(100% - 2px) center;
padding-right: 20px; padding-right: 20px;
height: 24px; height: 24px;
} }
[dir="rtl"] .maputnik-select { [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 // MULTIBUTTON
@@ -116,12 +119,12 @@
padding: 0; padding: 0;
.maputnik-button { .maputnik-button {
margin-right: $margin-1; margin-right: vars.$margin-1;
} }
} }
.maputnik-button-selected { .maputnik-button-selected {
background-color: color.adjust($color-midgray, $lightness: 12%); background-color: color.adjust(vars.$color-midgray, $lightness: 12%);
color: white; color: white;
} }
@@ -137,7 +140,7 @@
outline: none; outline: none;
&-wrapper { &-wrapper {
@extend .maputnik-input; /* stylelint-disable-line */ @extend .maputnik-input !optional; /* stylelint-disable-line */
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
@@ -154,12 +157,12 @@
text-align: center; text-align: center;
height: 24px; height: 24px;
width: 24px; width: 24px;
margin-right: $margin-2; margin-right: vars.$margin-2;
background-color: $color-gray; background-color: vars.$color-gray;
border-radius: 2px; border-radius: 2px;
border-style: solid; border-style: solid;
border-width: 2px; border-width: 2px;
border-color: $color-gray; border-color: vars.$color-gray;
transition: background-color 0.1s ease-out; transition: background-color 0.1s ease-out;
position: absolute; position: absolute;
top: 0; top: 0;
@@ -185,7 +188,7 @@
width: 50%; width: 50%;
height: 50%; height: 50%;
margin-top: 1px; margin-top: 1px;
fill: $color-lowgray; fill: vars.$color-lowgray;
} }
} }
@@ -198,29 +201,29 @@
position: absolute; position: absolute;
overflow: auto; overflow: auto;
max-height: 50%; max-height: 50%;
background: $color-gray; background: vars.$color-gray;
z-index: 3; z-index: 3;
} }
&-menu-item { &-menu-item {
user-select: none; user-select: none;
color: $color-lowgray; color: vars.$color-lowgray;
cursor: default; cursor: default;
padding: $margin-1; padding: vars.$margin-1;
font-size: $font-size-6; font-size: vars.$font-size-6;
z-index: 3; z-index: 3;
background: $color-gray; background: vars.$color-gray;
} }
&-menu-item-selected { &-menu-item-selected {
background: $color-midgray; background: vars.$color-midgray;
} }
} }
// FONT // FONT
.maputnik-font { .maputnik-font {
.maputnik-autocomplete:not(:last-child) { .maputnik-autocomplete:not(:last-child) {
margin-bottom: $margin-3; margin-bottom: vars.$margin-3;
} }
} }
@@ -236,21 +239,21 @@
.SpecDoc__sdk-support__table { .SpecDoc__sdk-support__table {
width: 100%; width: 100%;
margin-top: $margin-3; margin-top: vars.$margin-3;
td, th { td, th {
border: solid 1px $color-midgray; border: solid 1px vars.$color-midgray;
padding: 4px 6px; padding: 4px 6px;
white-space: nowrap; white-space: nowrap;
} }
} }
.SpecDoc__values li { .SpecDoc__values li {
margin-top: $margin-3; margin-top: vars.$margin-3;
} }
.SpecDoc__values code { .SpecDoc__values code {
background: $color-midgray; background: vars.$color-midgray;
padding: 0.1em 0.3em; padding: 0.1em 0.3em;
border-radius: 2px; border-radius: 2px;
} }
+50 -44
View File
@@ -1,3 +1,6 @@
@use "mixins";
@use "vars";
@use 'sass:color'; @use 'sass:color';
// LAYER LIST // LAYER LIST
.maputnik-layer-list { .maputnik-layer-list {
@@ -7,9 +10,9 @@
flex-direction: column; flex-direction: column;
&-header { &-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; flex: 0 0;
> * { > * {
@@ -19,8 +22,8 @@
} }
&-header-title { &-header-title {
font-size: $font-size-5; font-size: vars.$font-size-5;
color: $color-white; color: vars.$color-white;
font-weight: bold; font-weight: bold;
line-height: 1.3; line-height: 1.3;
} }
@@ -28,7 +31,7 @@
&-container { &-container {
padding: 0; padding: 0;
margin: 0; margin: 0;
padding-bottom: $margin-5; padding-bottom: vars.$margin-5;
flex: 1; flex: 1;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
@@ -47,9 +50,9 @@
&-item { &-item {
border: solid 1px transparent; border: solid 1px transparent;
font-weight: 400; font-weight: 400;
color: $color-lowgray; color: vars.$color-lowgray;
font-size: $font-size-6; font-size: vars.$font-size-6;
border-bottom-color: color.adjust($color-black, $lightness: 0.1%); border-bottom-color: color.adjust(vars.$color-black, $lightness: 0.1%);
user-select: none; user-select: none;
list-style: none; list-style: none;
z-index: 2000; z-index: 2000;
@@ -63,21 +66,22 @@
transition: opacity 600ms, visibility 600ms; transition: opacity 600ms, visibility 600ms;
&:focus-within { &:focus-within {
border: solid 1px $color-lowgray; border: solid 1px vars.$color-lowgray;
} }
@include mixins.flex-row;
@media screen and (prefers-reduced-motion: reduce) { @media screen and (prefers-reduced-motion: reduce) {
transition-duration: 0; transition-duration: 0;
} }
@include flex-row;
} }
&-icon-action { &-icon-action {
display: none; display: none;
svg { svg {
fill: $color-black; fill: vars.$color-black;
} }
} }
@@ -88,10 +92,10 @@
height: 15px; height: 15px;
svg { svg {
fill: color.adjust($color-lowgray, $lightness: -20%); fill: color.adjust(vars.$color-lowgray, $lightness: -20%);
&:hover { &:hover {
fill: $color-white; fill: vars.$color-white;
} }
} }
} }
@@ -102,24 +106,24 @@
.maputnik-layer-list-item:hover, .maputnik-layer-list-item:hover,
.maputnik-layer-list-item-selected { .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 { .maputnik-layer-list-icon-action {
display: block; display: block;
svg { svg {
fill: color.adjust($color-lowgray, $lightness: -0.5%); fill: color.adjust(vars.$color-lowgray, $lightness: -0.5%);
} }
} }
} }
.maputnik-layer-list-item--error { .maputnik-layer-list-item--error {
color: $color-red; color: vars.$color-red;
} }
&-item-selected { &-item-selected {
color: $color-white; color: vars.$color-white;
} }
&-item-collapsed { &-item-collapsed {
@@ -132,7 +136,7 @@
} }
&-item-group-last { &-item-group-last {
border-bottom: 2px solid $color-gray; border-bottom: 2px solid vars.$color-gray;
} }
&-item-id { &-item-id {
@@ -150,14 +154,14 @@
&-group-header { &-group-header {
border: solid 1px transparent; border: solid 1px transparent;
font-size: $font-size-6; font-size: vars.$font-size-6;
color: $color-lowgray; color: vars.$color-lowgray;
background-color: color.adjust($color-black, $lightness: 2%); background-color: color.adjust(vars.$color-black, $lightness: 2%);
user-select: none; user-select: none;
padding: $margin-2; padding: vars.$margin-2;
&:focus-within { &:focus-within {
border: solid 1px $color-lowgray; border: solid 1px vars.$color-lowgray;
} }
button { button {
@@ -165,7 +169,7 @@
cursor: pointer; cursor: pointer;
} }
@include flex-row; @include mixins.flex-row;
svg { svg {
width: 14px; width: 14px;
@@ -178,25 +182,27 @@
} }
&-group-content { &-group-content {
margin: 0 $margin-3; margin: 0 vars.$margin-3;
} }
} }
// FILTER EDITOR // FILTER EDITOR
.maputnik-layer-editor-group { .maputnik-layer-editor-group {
font-weight: bold; font-weight: bold;
font-size: $font-size-5; font-size: vars.$font-size-5;
background-color: color.adjust($color-black, $lightness: 2%); background-color: color.adjust(vars.$color-black, $lightness: 2%);
color: $color-white; color: vars.$color-white;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
line-height: 20px; line-height: 20px;
border-top: solid 1px #36383e; border-top: solid 1px #36383e;
@include mixins.flex-row;
&__button { &__button {
flex: 1; flex: 1;
display: flex; display: flex;
padding: $margin-2; padding: vars.$margin-2;
&__icon { &__icon {
fill: white; fill: white;
@@ -223,17 +229,17 @@
} }
@include flex-row;
&:hover { &:hover {
background-color: $color-gray; background-color: vars.$color-gray;
} }
} }
// PROPERTY // PROPERTY
.maputnik-default-property { .maputnik-default-property {
.maputnik-input-block-label { .maputnik-input-block-label {
color: color.adjust($color-lowgray, $lightness: -20%); color: color.adjust(vars.$color-lowgray, $lightness: -20%);
} }
.maputnik-string, .maputnik-string,
@@ -241,8 +247,8 @@
.maputnik-color, .maputnik-color,
.maputnik-select, .maputnik-select,
.maputnik-checkbox-wrapper { .maputnik-checkbox-wrapper {
background-color: color.adjust($color-gray, $lightness: -2%); background-color: color.adjust(vars.$color-gray, $lightness: -2%);
color: color.adjust($color-lowgray, $lightness: -20%); color: color.adjust(vars.$color-lowgray, $lightness: -20%);
} }
.maputnik-make-zoom-function svg { .maputnik-make-zoom-function svg {
@@ -250,18 +256,18 @@
} }
.maputnik-multibutton .maputnik-button { .maputnik-multibutton .maputnik-button {
background-color: color.adjust($color-midgray, $lightness: -10%); background-color: color.adjust(vars.$color-midgray, $lightness: -10%);
color: color.adjust($color-lowgray, $lightness: -20%); color: color.adjust(vars.$color-lowgray, $lightness: -20%);
&:hover { &:hover {
background-color: color.adjust($color-midgray, $lightness: 12%); background-color: color.adjust(vars.$color-midgray, $lightness: 12%);
color: $color-white; color: vars.$color-white;
} }
} }
.maputnik-multibutton .maputnik-button-selected { .maputnik-multibutton .maputnik-button-selected {
background-color: color.adjust($color-midgray, $lightness: -2%); background-color: color.adjust(vars.$color-midgray, $lightness: -2%);
color: $color-lowgray; color: vars.$color-lowgray;
} }
} }
@@ -276,8 +282,8 @@
&__menu { &__menu {
position: absolute; position: absolute;
z-index: 9999; z-index: 9999;
background: $color-black; background: vars.$color-black;
border: solid 1px $color-midgray; border: solid 1px vars.$color-midgray;
right: 0; right: 0;
min-width: 120px; min-width: 120px;
} }
@@ -295,7 +301,7 @@
.layer-header { .layer-header {
display: flex; display: flex;
padding: 6px; padding: 6px;
background: $color-black; background: vars.$color-black;
&__title { &__title {
flex: 1; flex: 1;
@@ -310,7 +316,7 @@
// Clone of the element which is sorted // Clone of the element which is sorted
.sortableHelper { .sortableHelper {
font-family: $font-family; font-family: vars.$font-family;
z-index: 9999; z-index: 9999;
border: none; border: none;
} }

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