mirror of
https://github.com/maputnik/editor.git
synced 2026-09-16 17:27:34 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b09c629be4 | |||
| 89be954485 | |||
| 83c099bef4 | |||
| 382ba69134 | |||
| dbe5d3916d | |||
| 373b57a6f4 | |||
| f2579e2a19 | |||
| aa9530274f | |||
| 0ba7b3704f | |||
| be11f75c9f | |||
| 03a41cf17a | |||
| 192df96a79 | |||
| 10d47c561b | |||
| c22f7404e7 | |||
| 36073b2b1f | |||
| ca66f47be0 | |||
| a5c47437ee | |||
| 91a6335d09 | |||
| acaa350f0f | |||
| 1a5c879c75 | |||
| b16776fed3 | |||
| d039dc73a5 | |||
| 70a2db580f | |||
| b76d9cbe29 | |||
| dd04c14d96 | |||
| 6c31add041 | |||
| 60ed80651c | |||
| c642ec5325 | |||
| 0c98378243 | |||
| 95ba3c4428 | |||
| b112c6a2bf | |||
| a251faf8f7 | |||
| 8580499ec3 | |||
| 56a9840def | |||
| 3d1919738b | |||
| cc317c20a1 | |||
| a9c3da0f40 | |||
| dc5d6dd77d | |||
| 95d87ad7ef | |||
| 09efbb5cd6 | |||
| 4f046cb83e | |||
| fb00dea285 | |||
| c00395ee11 | |||
| 5092a924aa | |||
| 7d80dccb81 | |||
| a93fe4d78b | |||
| 603a30cba8 | |||
| 6f55fda7d4 | |||
| 8f0ee898ff | |||
| 3896c4c187 | |||
| c0f1a72d80 | |||
| ac9186e7f8 | |||
| 8c86607100 | |||
| 7c5e5358cb | |||
| 18b34d3ecd | |||
| 08ebca266c | |||
| 8344a30f5d | |||
| 4249e9beb9 | |||
| c3dd253737 | |||
| f7b48139a4 | |||
| 8af1cfd5f8 |
@@ -31,10 +31,6 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
groups:
|
|
||||||
codeql-action:
|
|
||||||
patterns:
|
|
||||||
- "github/codeql-action*"
|
|
||||||
cooldown:
|
cooldown:
|
||||||
default-days: 3
|
default-days: 3
|
||||||
# no semver support for github-actions
|
# no semver support for github-actions
|
||||||
|
|||||||
+11
-15
@@ -6,10 +6,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build-node:
|
build-node:
|
||||||
@@ -25,9 +21,9 @@ jobs:
|
|||||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with: { persist-credentials: false }
|
with: { persist-credentials: false }
|
||||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
@@ -45,9 +41,9 @@ 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with: { persist-credentials: false }
|
with: { persist-credentials: false }
|
||||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
@@ -60,7 +56,7 @@ jobs:
|
|||||||
|
|
||||||
# Build and upload desktop CLI artifacts
|
# Build and upload desktop CLI artifacts
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
||||||
with:
|
with:
|
||||||
go-version: ^1.23.x
|
go-version: ^1.23.x
|
||||||
cache-dependency-path: desktop/go.sum
|
cache-dependency-path: desktop/go.sum
|
||||||
@@ -94,9 +90,9 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with: { persist-credentials: false }
|
with: { persist-credentials: false }
|
||||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
@@ -115,9 +111,9 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with: { persist-credentials: false }
|
with: { persist-credentials: false }
|
||||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
@@ -143,9 +139,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with: { persist-credentials: false }
|
with: { persist-credentials: false }
|
||||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '17 0 * * 6'
|
- cron: '17 0 * * 6'
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
@@ -42,11 +38,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -57,7 +53,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
uses: github/codeql-action/autobuild@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -71,4 +67,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Use Node.js from nvmrc
|
- name: Use Node.js from nvmrc
|
||||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
if: ${{ github.event_name == 'push' }}
|
if: ${{ github.event_name == 'push' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with: { persist-credentials: false }
|
with: { persist-credentials: false }
|
||||||
|
|
||||||
- name: Use Node.js from nvmrc
|
- name: Use Node.js from nvmrc
|
||||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
|
|
||||||
@@ -45,11 +45,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
|
||||||
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- 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
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: main
|
ref: main
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Use Node.js from nvmrc
|
- name: Use Node.js from nvmrc
|
||||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
|
|
||||||
@@ -42,19 +42,19 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Use Node.js from nvmrc
|
- name: Use Node.js from nvmrc
|
||||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
- name: Set up Go for desktop build
|
- name: Set up Go for desktop build
|
||||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
||||||
with:
|
with:
|
||||||
go-version: ^1.23.x
|
go-version: ^1.23.x
|
||||||
cache-dependency-path: desktop/go.sum
|
cache-dependency-path: desktop/go.sum
|
||||||
|
|||||||
@@ -2,18 +2,9 @@
|
|||||||
|
|
||||||
### ✨ Features and improvements
|
### ✨ Features and improvements
|
||||||
- Replace Cypress with Playwright for end-to-end tests and Vitest browser mode (Playwright provider) for component tests; drop the `@shellygo/cypress-test-utils` helper in favour of a `MaputnikDriver` page object
|
- Replace Cypress with Playwright for end-to-end tests and Vitest browser mode (Playwright provider) for component tests; drop the `@shellygo/cypress-test-utils` helper in favour of a `MaputnikDriver` page object
|
||||||
- Upgrade to MapLibre GL JS 6 and Vite 8, and update the remaining dependencies
|
|
||||||
- Serve the RTL text plugin from `@mapbox/mapbox-gl-rtl-text` instead of a pinned CDN URL, so its version is tracked in `package.json`
|
|
||||||
- Exported HTML now loads MapLibre GL JS as an ES module, since v6 no longer ships a UMD bundle
|
|
||||||
- The sidebar can now be resized, both as a whole and in the split between the layer list and the layer editor
|
|
||||||
- _...Add new stuff here..._
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
### 🐞 Bug fixes
|
### 🐞 Bug fixes
|
||||||
- Prevent a crash when converting a function to an expression
|
|
||||||
- Prevent a crash when deleting either stop from a function with two stops
|
|
||||||
- Preserve expanded layer groups when deleting layers, including the first layer of a group
|
|
||||||
- The map's data listener now fires on tile loads again, so source and vector layer field autocompletion is populated
|
|
||||||
- The `maputnik` desktop binary now opens the default browser automatically on startup (opt out with `--no-browser`)
|
|
||||||
- _...Add new stuff here..._
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
## 3.1.0
|
## 3.1.0
|
||||||
|
|||||||
+8
-3
@@ -7,16 +7,21 @@ require (
|
|||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/maputnik/desktop v1.0.7
|
github.com/maputnik/desktop v1.0.7
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
|
||||||
github.com/urfave/cli v1.22.12
|
github.com/urfave/cli v1.22.12
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/GeertJohan/go.incremental v1.0.0 // indirect
|
||||||
|
github.com/akavel/rsrc v0.8.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/daaku/go.zipexe v1.0.2 // indirect
|
github.com/daaku/go.zipexe v1.0.2 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||||
|
github.com/jessevdk/go-flags v1.4.0 // indirect
|
||||||
|
github.com/nkovacs/streamquote v1.0.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
golang.org/x/sys v0.1.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v1.0.1 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
+9
-6
@@ -1,7 +1,9 @@
|
|||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
||||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||||
github.com/GeertJohan/go.rice v1.0.3 h1:k5viR+xGtIhF61125vCE1cmJ5957RQGXG6dmbaWZSmI=
|
github.com/GeertJohan/go.rice v1.0.3 h1:k5viR+xGtIhF61125vCE1cmJ5957RQGXG6dmbaWZSmI=
|
||||||
github.com/GeertJohan/go.rice v1.0.3/go.mod h1:XVdrU4pW00M4ikZed5q56tPf1v2KwnIKeIdc9CBYNt4=
|
github.com/GeertJohan/go.rice v1.0.3/go.mod h1:XVdrU4pW00M4ikZed5q56tPf1v2KwnIKeIdc9CBYNt4=
|
||||||
|
github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw=
|
||||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
@@ -18,14 +20,14 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH
|
|||||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/maputnik/desktop v1.0.7 h1:rdFg7emIJOT3YsZpwqSChmWtMOvu+T4h6WwVQAZP9n4=
|
github.com/maputnik/desktop v1.0.7 h1:rdFg7emIJOT3YsZpwqSChmWtMOvu+T4h6WwVQAZP9n4=
|
||||||
github.com/maputnik/desktop v1.0.7/go.mod h1:wmDjHUztx9jOBz0I22589yWguAGdV/sEM57YANpN8oQ=
|
github.com/maputnik/desktop v1.0.7/go.mod h1:wmDjHUztx9jOBz0I22589yWguAGdV/sEM57YANpN8oQ=
|
||||||
|
github.com/nkovacs/streamquote v1.0.0 h1:PmVIV08Zlx2lZK5fFZlMZ04eHcDTIFJCv/5/0twVUow=
|
||||||
github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc=
|
github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc=
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
@@ -39,11 +41,12 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
|
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
|
||||||
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
|
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
+2
-26
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -10,7 +9,6 @@ import (
|
|||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/maputnik/desktop/filewatch"
|
"github.com/maputnik/desktop/filewatch"
|
||||||
"github.com/pkg/browser"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,10 +36,6 @@ func main() {
|
|||||||
Name: "static",
|
Name: "static",
|
||||||
Usage: "Serve directory under /static/",
|
Usage: "Serve directory under /static/",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "no-browser",
|
|
||||||
Usage: "Do not automatically open the default browser",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Action = func(c *cli.Context) error {
|
app.Action = func(c *cli.Context) error {
|
||||||
@@ -77,26 +71,8 @@ func main() {
|
|||||||
loggedRouter := handlers.LoggingHandler(os.Stdout, router)
|
loggedRouter := handlers.LoggingHandler(os.Stdout, router)
|
||||||
corsRouter := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}), handlers.AllowedMethods([]string{"GET", "PUT"}), handlers.AllowedOrigins([]string{"*"}), handlers.AllowCredentials())(loggedRouter)
|
corsRouter := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}), handlers.AllowedMethods([]string{"GET", "PUT"}), handlers.AllowedOrigins([]string{"*"}), handlers.AllowCredentials())(loggedRouter)
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", c.Int("port")))
|
fmt.Printf("Exposing Maputnik on http://localhost:%d\n", c.Int("port"))
|
||||||
if err != nil {
|
return http.ListenAndServe(fmt.Sprintf(":%d", c.Int("port")), corsRouter)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("http://localhost:%d", c.Int("port"))
|
|
||||||
fmt.Printf("Exposing Maputnik on %s\n", url)
|
|
||||||
|
|
||||||
// Listener is already accepting connections, so this can't race http.Serve below.
|
|
||||||
// xdg-open is known to hang on some headless Linux setups, so this runs in its own
|
|
||||||
// goroutine to keep a stuck opener from stalling server startup.
|
|
||||||
if !c.Bool("no-browser") {
|
|
||||||
go func() {
|
|
||||||
if err := browser.OpenURL(url); err != nil {
|
|
||||||
fmt.Printf("Could not open browser automatically: %s\nPlease open %s manually.\n", err, url)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.Serve(listener, corsRouter)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Run(os.Args)
|
app.Run(os.Args)
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 8,
|
|
||||||
"name": "Grouped layers",
|
|
||||||
"sources": {},
|
|
||||||
"layers": [
|
|
||||||
{ "id": "background", "type": "background" },
|
|
||||||
{ "id": "park-a", "type": "background" },
|
|
||||||
{ "id": "park-b", "type": "background" },
|
|
||||||
{ "id": "park-c", "type": "background" },
|
|
||||||
{ "id": "road", "type": "background" },
|
|
||||||
{ "id": "park-d", "type": "background" },
|
|
||||||
{ "id": "park-e", "type": "background" },
|
|
||||||
{ "id": "water-a", "type": "background" },
|
|
||||||
{ "id": "water-b", "type": "background" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -289,20 +289,6 @@ describe("layer editor", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should leave a plain editable value after deleting a stop", async () => {
|
|
||||||
await when.setFunctionStopValue("circle-radius", "Output value", 1, "0");
|
|
||||||
await when.deleteFunctionStop("circle-radius");
|
|
||||||
await then(get.elementByTestId("spec-field:circle-radius")).shouldBeVisible();
|
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
|
||||||
layers: [{ id, paint: { "circle-radius": 0 } }],
|
|
||||||
});
|
|
||||||
|
|
||||||
await when.setValue("spec-field-input:circle-radius", "7");
|
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
|
||||||
layers: [{ id, paint: { "circle-radius": 7 } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should set the base", async () => {
|
test("should set the base", async () => {
|
||||||
await when.setFunctionBase("circle-radius", "2");
|
await when.setFunctionBase("circle-radius", "2");
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
@@ -324,9 +310,8 @@ describe("layer editor", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should convert to an expression without crashing", async () => {
|
test("should convert to an expression", async () => {
|
||||||
await when.makeExpression("circle-radius");
|
await when.makeExpression("circle-radius");
|
||||||
await then(get.element("[data-wd-key='spec-field-container:circle-radius'] .maputnik-expression-editor")).shouldBeVisible();
|
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
layers: [{ id, paint: { "circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 5, 10, 5] } }],
|
layers: [{ id, paint: { "circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 5, 10, 5] } }],
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-78
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, expect, test } from "./utils/fixtures";
|
import { beforeEach, describe, test } from "./utils/fixtures";
|
||||||
import { MaputnikDriver } from "./maputnik-driver";
|
import { MaputnikDriver } from "./maputnik-driver";
|
||||||
|
|
||||||
describe("layers list", () => {
|
describe("layers list", () => {
|
||||||
@@ -47,14 +47,6 @@ describe("layers list", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should title the visibility button with the action it performs", async () => {
|
|
||||||
const key = "layer-list-item:" + id + ":toggle-visibility";
|
|
||||||
// A visible layer is about to be hidden, and a hidden one to be shown.
|
|
||||||
await expect(get.elementByTestId(key)).toHaveAttribute("title", "hide");
|
|
||||||
await when.click(key);
|
|
||||||
await expect(get.elementByTestId(key)).toHaveAttribute("title", "show");
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when clicking hide", () => {
|
describe("when clicking hide", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await when.click("layer-list-item:" + id + ":toggle-visibility");
|
await when.click("layer-list-item:" + id + ":toggle-visibility");
|
||||||
@@ -306,75 +298,6 @@ describe("layers list", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("groups", () => {
|
describe("groups", () => {
|
||||||
for (const control of ["menu", "trash"] as const) {
|
|
||||||
describe(`after deletion using ${control}`, () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await when.setStyle("grouped_layers");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("keeps all groups expanded when an earlier layer is deleted", async () => {
|
|
||||||
await when.click("skip-target-layer-list");
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
|
||||||
await when.deleteLayer("background", control);
|
|
||||||
await then(get.elementByTestId("layer-list-item:background")).shouldNotExist();
|
|
||||||
await then(get.elementByTestId("layer-list-group:park-0")).shouldExist();
|
|
||||||
await expect(get.elementByTestId("layer-list-group:water-6").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-e")).shouldBeVisible();
|
|
||||||
await then(get.elementByTestId("layer-list-item:water-b")).shouldBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("preserves separate states for groups with the same prefix", async () => {
|
|
||||||
await when.click("layer-list-group:park-1");
|
|
||||||
await when.deleteLayer("background", control);
|
|
||||||
await then(get.elementByTestId("layer-list-group:park-0")).shouldExist();
|
|
||||||
await when.click("layer-list-item:road");
|
|
||||||
await expect(get.elementByTestId("layer-list-group:park-0").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
|
||||||
await expect(get.elementByTestId("layer-list-group:park-4").getByRole("button")).toHaveAttribute("aria-expanded", "false");
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-e")).shouldNotBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("keeps a group expanded when its first layer is deleted", async () => {
|
|
||||||
await when.click("layer-list-group:park-1");
|
|
||||||
await when.deleteLayer("park-a", control);
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-a")).shouldNotExist();
|
|
||||||
await when.click("layer-list-item:road");
|
|
||||||
await expect(get.elementByTestId("layer-list-group:park-1").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-c")).shouldBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const expandedGroup of ["park-1", "park-5"]) {
|
|
||||||
test(`keeps a merged group expanded when ${expandedGroup} was expanded`, async () => {
|
|
||||||
await when.click("layer-list-group:" + expandedGroup);
|
|
||||||
await when.deleteLayer("road", control);
|
|
||||||
await then(get.elementByTestId("layer-list-item:road")).shouldNotExist();
|
|
||||||
await expect(get.elementByTestId("layer-list-group:park-1").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-b")).shouldBeVisible();
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-e")).shouldBeVisible();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
test("keeps a merged group collapsed when both groups were collapsed", async () => {
|
|
||||||
await when.deleteLayer("road", control);
|
|
||||||
await then(get.elementByTestId("layer-list-item:road")).shouldNotExist();
|
|
||||||
await when.click("layer-list-item:background");
|
|
||||||
await expect(get.elementByTestId("layer-list-group:park-1").getByRole("button")).toHaveAttribute("aria-expanded", "false");
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-b")).shouldNotBeVisible();
|
|
||||||
await then(get.elementByTestId("layer-list-item:park-e")).shouldNotBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("preserves later groups after deleting an entire group", async () => {
|
|
||||||
await when.click("skip-target-layer-list");
|
|
||||||
await when.deleteLayer("park-a", control);
|
|
||||||
await when.deleteLayer("park-b", control);
|
|
||||||
await when.deleteLayer("park-c", control);
|
|
||||||
await expect(get.elementByTestId("layer-list-group:water-4").getByRole("button")).toHaveAttribute("aria-expanded", "true");
|
|
||||||
await then(get.elementByTestId("layer-list-item:water-b")).shouldBeVisible();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
test("simple", async () => {
|
test("simple", async () => {
|
||||||
await when.setStyle("geojson");
|
await when.setStyle("geojson");
|
||||||
|
|
||||||
|
|||||||
+1
-22
@@ -39,7 +39,6 @@ export class MaputnikDriver {
|
|||||||
"example-style-with-zoom-7-and-center-0-51.json",
|
"example-style-with-zoom-7-and-center-0-51.json",
|
||||||
"example-style-with-zoom-5-and-center-50-50.json",
|
"example-style-with-zoom-5-and-center-50-50.json",
|
||||||
"access-token-style.json",
|
"access-token-style.json",
|
||||||
"grouped-layers-style.json",
|
|
||||||
];
|
];
|
||||||
for (const fixture of styleFixtures) {
|
for (const fixture of styleFixtures) {
|
||||||
await this.helper.given.interceptAndMockResponse({
|
await this.helper.given.interceptAndMockResponse({
|
||||||
@@ -64,18 +63,6 @@ export class MaputnikDriver {
|
|||||||
|
|
||||||
modal: this.modalDriver.when,
|
modal: this.modalDriver.when,
|
||||||
|
|
||||||
deleteLayer: async (id: string, control: "menu" | "trash" = "menu") => {
|
|
||||||
if (control === "trash") {
|
|
||||||
await this.helper.when.hover("layer-list-item:" + id);
|
|
||||||
await this.helper.when.click("layer-list-item:" + id + ":delete");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.helper.when.click("layer-list-item:" + id);
|
|
||||||
await this.helper.when.click("skip-target-layer-editor");
|
|
||||||
await this.helper.when.click("menu-delete-layer");
|
|
||||||
},
|
|
||||||
|
|
||||||
setStyle: async (
|
setStyle: async (
|
||||||
styleProperties:
|
styleProperties:
|
||||||
| "geojson"
|
| "geojson"
|
||||||
@@ -86,7 +73,6 @@ export class MaputnikDriver {
|
|||||||
| "font"
|
| "font"
|
||||||
| "zoom_7_center_0_51"
|
| "zoom_7_center_0_51"
|
||||||
| "access_tokens"
|
| "access_tokens"
|
||||||
| "grouped_layers"
|
|
||||||
| "",
|
| "",
|
||||||
zoom?: number
|
zoom?: number
|
||||||
) => {
|
) => {
|
||||||
@@ -99,7 +85,6 @@ export class MaputnikDriver {
|
|||||||
font: "example-style-with-fonts.json",
|
font: "example-style-with-fonts.json",
|
||||||
zoom_7_center_0_51: "example-style-with-zoom-7-and-center-0-51.json",
|
zoom_7_center_0_51: "example-style-with-zoom-7-and-center-0-51.json",
|
||||||
access_tokens: "access-token-style.json",
|
access_tokens: "access-token-style.json",
|
||||||
grouped_layers: "grouped-layers-style.json",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = new URL(baseUrl);
|
const url = new URL(baseUrl);
|
||||||
@@ -125,13 +110,7 @@ export class MaputnikDriver {
|
|||||||
},
|
},
|
||||||
|
|
||||||
chooseExampleFile: async () => {
|
chooseExampleFile: async () => {
|
||||||
await this.helper.when.openFileByFixture("example-style.json", "modal:open.dropzone");
|
await this.helper.when.openFileByFixture("example-style.json", "modal:open.dropzone", "modal:open.file.input");
|
||||||
await this.helper.when.wait(200);
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Picks the example style through the browser's native file chooser. */
|
|
||||||
chooseExampleFileFromPicker: async () => {
|
|
||||||
await this.helper.when.chooseFileFromPicker("example-style.json", "modal:open.dropzone");
|
|
||||||
await this.helper.when.wait(200);
|
await this.helper.when.wait(200);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -43,16 +43,6 @@ describe("modals", () => {
|
|||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude(get.responseBody("example-style.json"));
|
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude(get.responseBody("example-style.json"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("without the File System Access API", () => {
|
|
||||||
test("upload via the file chooser", async () => {
|
|
||||||
await given.noFileSystemAccessApi();
|
|
||||||
await when.setStyle("");
|
|
||||||
await when.click("nav:open");
|
|
||||||
await when.chooseExampleFileFromPicker();
|
|
||||||
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude(get.fixture("example-style.json"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("shortcuts", () => {
|
describe("shortcuts", () => {
|
||||||
|
|||||||
@@ -122,8 +122,6 @@ export class Assertable<T> {
|
|||||||
// Value assertions (auto-retrying for Query targets).
|
// Value assertions (auto-retrying for Query targets).
|
||||||
shouldEqual = (value: any) => this.assertValue((actual) => expect(actual).toBe(value));
|
shouldEqual = (value: any) => this.assertValue((actual) => expect(actual).toBe(value));
|
||||||
|
|
||||||
shouldBeGreaterThan = (value: number) => this.assertValue((actual) => expect(actual).toBeGreaterThan(value));
|
|
||||||
|
|
||||||
shouldInclude = (value: any) =>
|
shouldInclude = (value: any) =>
|
||||||
this.assertValue((actual) => {
|
this.assertValue((actual) => {
|
||||||
if (typeof value === "object" && value !== null) {
|
if (typeof value === "object" && value !== null) {
|
||||||
@@ -220,18 +218,6 @@ export class PlaywrightHelper {
|
|||||||
public then = <T>(target: T): Assertable<T> => new Assertable(target);
|
public then = <T>(target: T): Assertable<T> => new Assertable(target);
|
||||||
|
|
||||||
public given = {
|
public given = {
|
||||||
/**
|
|
||||||
* Removes the File System Access API so the app falls back to a plain
|
|
||||||
* <input type="file">, the way Firefox and Safari behave. Must be called
|
|
||||||
* before the page under test is loaded.
|
|
||||||
*/
|
|
||||||
noFileSystemAccessApi: async () => {
|
|
||||||
await this.page.addInitScript(() => {
|
|
||||||
delete (window as any).showOpenFilePicker;
|
|
||||||
delete (window as any).showSaveFilePicker;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
intercept: async (pattern: RegExp, alias: string, _method = "GET") => {
|
intercept: async (pattern: RegExp, alias: string, _method = "GET") => {
|
||||||
this.recordedRequests.set(alias, []);
|
this.recordedRequests.set(alias, []);
|
||||||
await this.page.route(pattern, (route) => {
|
await this.page.route(pattern, (route) => {
|
||||||
@@ -351,15 +337,6 @@ export class PlaywrightHelper {
|
|||||||
await this.page.mouse.up();
|
await this.page.mouse.up();
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Presses at the centre of an element and drags it by the given offset. */
|
|
||||||
dragBy: async (testId: string, deltaX: number, deltaY = 0) => {
|
|
||||||
const { x, y } = await centerOf(this.testId(testId));
|
|
||||||
await this.page.mouse.move(x, y);
|
|
||||||
await this.page.mouse.down();
|
|
||||||
await this.page.mouse.move(x + deltaX, y + deltaY, { steps: 10 });
|
|
||||||
await this.page.mouse.up();
|
|
||||||
},
|
|
||||||
|
|
||||||
clickCenter: async (testId: string) => {
|
clickCenter: async (testId: string) => {
|
||||||
const { x, y } = await centerOf(this.testId(testId));
|
const { x, y } = await centerOf(this.testId(testId));
|
||||||
await this.page.mouse.move(x, y);
|
await this.page.mouse.move(x, y);
|
||||||
@@ -368,38 +345,23 @@ export class PlaywrightHelper {
|
|||||||
await this.page.mouse.up();
|
await this.page.mouse.up();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
openFileByFixture: async (fixture: string, buttonTestId: string, inputTestId: string) => {
|
||||||
* Opens a fixture through the File System Access API, which raises no
|
|
||||||
* "filechooser" event and so has to be stubbed. For the <input type="file">
|
|
||||||
* fallback that browsers without the API use, see chooseFileFromPicker.
|
|
||||||
*/
|
|
||||||
openFileByFixture: async (fixture: string, buttonTestId: string) => {
|
|
||||||
const content = JSON.stringify(this.readFixture(fixture));
|
const content = JSON.stringify(this.readFixture(fixture));
|
||||||
|
const hasPicker = await this.page.evaluate(() => "showOpenFilePicker" in window);
|
||||||
|
if (hasPicker) {
|
||||||
await this.page.evaluate((fileContent) => {
|
await this.page.evaluate((fileContent) => {
|
||||||
(window as any).showOpenFilePicker = async () => [
|
(window as any).showOpenFilePicker = async () => [
|
||||||
{ getFile: async () => ({ text: async () => fileContent }) },
|
{ getFile: async () => ({ text: async () => fileContent }) },
|
||||||
];
|
];
|
||||||
}, content);
|
}, content);
|
||||||
await this.testId(buttonTestId).click();
|
await this.testId(buttonTestId).click();
|
||||||
},
|
} else {
|
||||||
|
await this.testId(inputTestId).setInputFiles({
|
||||||
/**
|
|
||||||
* Clicks a control that opens the browser's native file chooser and answers
|
|
||||||
* it with a fixture. Only works on the <input type="file"> path — the File
|
|
||||||
* System Access API does not raise a "filechooser" event, so pair this with
|
|
||||||
* given.noFileSystemAccessApi().
|
|
||||||
*/
|
|
||||||
chooseFileFromPicker: async (fixture: string, triggerTestId: string) => {
|
|
||||||
const content = JSON.stringify(this.readFixture(fixture));
|
|
||||||
const [fileChooser] = await Promise.all([
|
|
||||||
this.page.waitForEvent("filechooser"),
|
|
||||||
this.testId(triggerTestId).click(),
|
|
||||||
]);
|
|
||||||
await fileChooser.setFiles({
|
|
||||||
name: fixture,
|
name: fixture,
|
||||||
mimeType: "application/json",
|
mimeType: "application/json",
|
||||||
buffer: Buffer.from(content),
|
buffer: Buffer.from(content),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
dropFileByFixture: async (fixture: string, dropzoneTestId: string) => {
|
dropFileByFixture: async (fixture: string, dropzoneTestId: string) => {
|
||||||
@@ -457,13 +419,6 @@ export class PlaywrightHelper {
|
|||||||
|
|
||||||
inputValue: (testId: string) => new Query<string>(() => this.testId(testId).first().inputValue()),
|
inputValue: (testId: string) => new Query<string>(() => this.testId(testId).first().inputValue()),
|
||||||
|
|
||||||
elementWidth: (testId: string) =>
|
|
||||||
new Query<number>(async () => {
|
|
||||||
const box = await this.testId(testId).first().boundingBox();
|
|
||||||
if (!box) throw new Error(`Element "${testId}" has no bounding box`);
|
|
||||||
return box.width;
|
|
||||||
}),
|
|
||||||
|
|
||||||
elementsText: (testId: string) => new Query<string>(() => this.testId(testId).first().innerText()),
|
elementsText: (testId: string) => new Query<string>(() => this.testId(testId).first().innerText()),
|
||||||
|
|
||||||
locationHash: () => new Query<string>(async () => new URL(this.page.url()).hash),
|
locationHash: () => new Query<string>(async () => new URL(this.page.url()).hash),
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { beforeEach, describe, test } from "./utils/fixtures";
|
|
||||||
import { MaputnikDriver } from "./maputnik-driver";
|
|
||||||
|
|
||||||
describe("sidebar resize", () => {
|
|
||||||
const { given, get, when, then } = new MaputnikDriver();
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await given.setupMockBackedResponses();
|
|
||||||
await when.setStyle("layer");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("dragging the outer handle widens the sidebar", async () => {
|
|
||||||
const initialWidth = await get.elementWidth("sidebar-panel").get();
|
|
||||||
|
|
||||||
await when.dragBy("sidebar-resize-handle", 100);
|
|
||||||
|
|
||||||
await then(get.elementWidth("sidebar-panel")).shouldBeGreaterThan(initialWidth + 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("dragging the inner handle widens the layer list", async () => {
|
|
||||||
const initialWidth = await get.elementWidth("layer-list-panel").get();
|
|
||||||
|
|
||||||
await when.dragBy("inner-resize-handle", 50);
|
|
||||||
|
|
||||||
await then(get.elementWidth("layer-list-panel")).shouldBeGreaterThan(initialWidth + 20);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+1
-4
@@ -14,7 +14,6 @@ export default defineConfig({
|
|||||||
files: ["**/*.{js,jsx,ts,tsx}"],
|
files: ["**/*.{js,jsx,ts,tsx}"],
|
||||||
ignores: [
|
ignores: [
|
||||||
"dist/**/*",
|
"dist/**/*",
|
||||||
"public/**/*",
|
|
||||||
],
|
],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 2024,
|
ecmaVersion: 2024,
|
||||||
@@ -35,9 +34,7 @@ export default defineConfig({
|
|||||||
rules: {
|
rules: {
|
||||||
"react-refresh/only-export-components": [
|
"react-refresh/only-export-components": [
|
||||||
"warn",
|
"warn",
|
||||||
// Many components are exported as withTranslation()(Component); without
|
{ allowConstantExport: true }
|
||||||
// this the rule cannot tell the HOC's result is still a component.
|
|
||||||
{ allowConstantExport: true, extraHOCs: ["withTranslation"] }
|
|
||||||
],
|
],
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { defineConfig } from "i18next-cli";
|
import { defineConfig } from "i18next-cli";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
locales: ["az", "de", "fr", "he", "it", "ja", "ko", "tr", "zh"],
|
locales: ["de", "fr", "he", "it", "ja", "ko", "tr", "zh"],
|
||||||
extract: {
|
extract: {
|
||||||
input: ["src/**/*.{js,jsx,ts,tsx}"],
|
input: ["src/**/*.{js,jsx,ts,tsx}"],
|
||||||
output: "src/locales/{{language}}/{{namespace}}.json",
|
output: "src/locales/{{language}}/{{namespace}}.json",
|
||||||
|
|||||||
Generated
+2027
-1427
File diff suppressed because it is too large
Load Diff
+33
-35
@@ -5,11 +5,10 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "''",
|
"main": "''",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"copy-rtl-text-plugin": "mkdir -p public && cp -p node_modules/@mapbox/mapbox-gl-rtl-text/dist/mapbox-gl-rtl-text.js public/",
|
"start": "vite",
|
||||||
"start": "npm run copy-rtl-text-plugin && vite",
|
"build": "tsc && vite build --mode=production",
|
||||||
"build": "npm run copy-rtl-text-plugin && tsc && vite build --mode=production",
|
"build-desktop": "tsc && vite build --mode=desktop && cd desktop && make",
|
||||||
"build-desktop": "npm run copy-rtl-text-plugin && tsc && vite build --mode=desktop && cd desktop && make",
|
"build-linux": "tsc && vite build --mode=desktop && cd desktop && make bin/linux/maputnik",
|
||||||
"build-linux": "npm run copy-rtl-text-plugin && tsc && vite build --mode=desktop && cd desktop && make bin/linux/maputnik",
|
|
||||||
"i18n:extract": "npx i18next-cli extract",
|
"i18n:extract": "npx i18next-cli extract",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"test": "playwright test",
|
"test": "playwright test",
|
||||||
@@ -29,16 +28,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-json": "^6.0.2",
|
"@codemirror/lang-json": "^6.0.2",
|
||||||
"@codemirror/lint": "^6.9.7",
|
"@codemirror/lint": "^6.9.7",
|
||||||
"@codemirror/state": "^6.7.4",
|
"@codemirror/state": "^6.7.1",
|
||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@codemirror/view": "^6.43.11",
|
"@codemirror/view": "^6.43.6",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "^0.4.0",
|
"@mapbox/mapbox-gl-rtl-text": "^0.4.0",
|
||||||
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
|
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
|
||||||
"@maplibre/maplibre-gl-inspect": "^1.9.0",
|
"@maplibre/maplibre-gl-inspect": "^1.8.2",
|
||||||
"@maplibre/maplibre-gl-style-spec": "^26.4.2",
|
"@maplibre/maplibre-gl-style-spec": "^25.0.2",
|
||||||
"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",
|
||||||
@@ -48,9 +47,9 @@
|
|||||||
"downshift": "^9.4.0",
|
"downshift": "^9.4.0",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18next": "^26.4.2",
|
"i18next": "^26.3.4",
|
||||||
"i18next-browser-languagedetector": "^8.2.1",
|
"i18next-browser-languagedetector": "^8.2.1",
|
||||||
"i18next-resources-to-backend": "^1.2.3",
|
"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",
|
||||||
"lodash": "^4.18.1",
|
"lodash": "^4.18.1",
|
||||||
@@ -60,23 +59,22 @@
|
|||||||
"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": "^6.7.0",
|
"maplibre-gl": "^5.24.0",
|
||||||
"maputnik-design": "github:maputnik/design#172b06c",
|
"maputnik-design": "github:maputnik/design#172b06c",
|
||||||
"ol": "^10.10.0",
|
"ol": "^10.9.0",
|
||||||
"ol-mapbox-style": "^13.4.3",
|
"ol-mapbox-style": "^13.4.1",
|
||||||
"pmtiles": "^4.5.0",
|
"pmtiles": "^4.4.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^19.2.8",
|
"react": "^19.2.7",
|
||||||
"react-accessible-accordion": "^5.0.1",
|
"react-accessible-accordion": "^5.0.1",
|
||||||
"react-aria-menubutton": "^8.0.0",
|
"react-aria-menubutton": "^8.0.0",
|
||||||
"react-aria-modal": "^5.0.2",
|
"react-aria-modal": "^5.0.2",
|
||||||
"react-collapse": "^5.1.1",
|
"react-collapse": "^5.1.1",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-dom": "^19.2.8",
|
"react-dom": "^19.2.7",
|
||||||
"react-i18next": "^17.0.13",
|
"react-i18next": "^17.0.8",
|
||||||
"react-icons": "^5.7.0",
|
"react-icons": "^5.7.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-resizable-panels": "^4.11.0",
|
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"slugify": "^1.6.9",
|
"slugify": "^1.6.9",
|
||||||
"string-hash": "^1.1.3",
|
"string-hash": "^1.1.3",
|
||||||
@@ -102,9 +100,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
"@playwright/test": "^1.63.0",
|
"@playwright/test": "^1.61.1",
|
||||||
"@stylistic/eslint-plugin": "^5.10.0",
|
"@stylistic/eslint-plugin": "^5.10.0",
|
||||||
"@types/codemirror": "^5.60.18",
|
"@types/codemirror": "^5.60.17",
|
||||||
"@types/color": "^4.2.1",
|
"@types/color": "^4.2.1",
|
||||||
"@types/cors": "^2.8.19",
|
"@types/cors": "^2.8.19",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
@@ -117,36 +115,36 @@
|
|||||||
"@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/randomcolor": "^0.5.9",
|
"@types/randomcolor": "^0.5.9",
|
||||||
"@types/react": "^19.2.18",
|
"@types/react": "^19.2.17",
|
||||||
"@types/react-aria-menubutton": "^6.2.14",
|
"@types/react-aria-menubutton": "^6.2.14",
|
||||||
"@types/react-aria-modal": "^5.0.0",
|
"@types/react-aria-modal": "^5.0.0",
|
||||||
"@types/react-collapse": "^5.0.4",
|
"@types/react-collapse": "^5.0.4",
|
||||||
"@types/react-color": "^3.0.13",
|
"@types/react-color": "^3.0.13",
|
||||||
"@types/react-dom": "^19.2.7",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@types/string-hash": "^1.1.3",
|
"@types/string-hash": "^1.1.3",
|
||||||
"@types/wicg-file-system-access": "^2023.10.7",
|
"@types/wicg-file-system-access": "^2023.10.7",
|
||||||
"@vitejs/plugin-react": "^6.1.1",
|
"@vitejs/plugin-react": "5.2",
|
||||||
"@vitest/coverage-v8": "^5.0.0",
|
"@vitest/coverage-v8": "^4.1.10",
|
||||||
"cors": "^2.8.6",
|
"cors": "^2.8.6",
|
||||||
"eslint": "^10.10.0",
|
"eslint": "^10.6.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^7.1.1",
|
"eslint-plugin-react-hooks": "^7.1.1",
|
||||||
"eslint-plugin-react-refresh": "^0.5.6",
|
"eslint-plugin-react-refresh": "^0.5.3",
|
||||||
"i18next-cli": "^1.73.1",
|
"i18next-cli": "^1.65.0",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"istanbul-lib-coverage": "^3.2.2",
|
"istanbul-lib-coverage": "^3.2.2",
|
||||||
"nyc": "^18.0.0",
|
"nyc": "^18.0.0",
|
||||||
"postcss": "^8.5.28",
|
"postcss": "^8.5.16",
|
||||||
"react-hot-loader": "^4.13.1",
|
"react-hot-loader": "^4.13.1",
|
||||||
"sass": "^1.104.0",
|
"sass": "^1.101.0",
|
||||||
"stylelint": "^17.15.0",
|
"stylelint": "^17.14.0",
|
||||||
"stylelint-config-recommended-scss": "^17.0.1",
|
"stylelint-config-recommended-scss": "^17.0.1",
|
||||||
"stylelint-scss": "^7.2.0",
|
"stylelint-scss": "^7.2.0",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"typescript-eslint": "^8.69.0",
|
"typescript-eslint": "^8.62.1",
|
||||||
"uuid": "^14.0.2",
|
"uuid": "^14.0.1",
|
||||||
"vite": "^8.2.2",
|
"vite": "^7.3.2",
|
||||||
"vite-plugin-istanbul": "^9.0.1",
|
"vite-plugin-istanbul": "^9.0.1",
|
||||||
"vitest": "^5.0.0"
|
"vitest": "^4.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-17
@@ -11,29 +11,29 @@ import {type Map, type LayerSpecification, type StyleSpecification, type Validat
|
|||||||
import {validateStyleMin} from "@maplibre/maplibre-gl-style-spec";
|
import {validateStyleMin} from "@maplibre/maplibre-gl-style-spec";
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
|
|
||||||
import { MapMaplibreGl } from "./MapMaplibreGl";
|
import MapMaplibreGl from "./MapMaplibreGl";
|
||||||
import { MapOpenLayers } from "./MapOpenLayers";
|
import MapOpenLayers from "./MapOpenLayers";
|
||||||
import { CodeEditor } from "./CodeEditor";
|
import CodeEditor from "./CodeEditor";
|
||||||
import { LayerList } from "./LayerList";
|
import LayerList from "./LayerList";
|
||||||
import { LayerEditor } from "./LayerEditor";
|
import LayerEditor from "./LayerEditor";
|
||||||
import { AppToolbar, type MapState } from "./AppToolbar";
|
import AppToolbar, { type MapState } from "./AppToolbar";
|
||||||
import { AppLayout } from "./AppLayout";
|
import AppLayout from "./AppLayout";
|
||||||
import { AppMessagePanel as MessagePanel } from "./AppMessagePanel";
|
import MessagePanel from "./AppMessagePanel";
|
||||||
|
|
||||||
import { ModalSettings } from "./modals/ModalSettings";
|
import ModalSettings from "./modals/ModalSettings";
|
||||||
import { ModalExport } from "./modals/ModalExport";
|
import ModalExport from "./modals/ModalExport";
|
||||||
import { ModalSources } from "./modals/ModalSources";
|
import ModalSources from "./modals/ModalSources";
|
||||||
import { ModalOpen } from "./modals/ModalOpen";
|
import ModalOpen from "./modals/ModalOpen";
|
||||||
import { ModalShortcuts } from "./modals/ModalShortcuts";
|
import ModalShortcuts from "./modals/ModalShortcuts";
|
||||||
import { ModalDebug } from "./modals/ModalDebug";
|
import ModalDebug from "./modals/ModalDebug";
|
||||||
import { ModalGlobalState } from "./modals/ModalGlobalState";
|
import ModalGlobalState from "./modals/ModalGlobalState";
|
||||||
|
|
||||||
import {downloadGlyphsMetadata, downloadSpriteMetadata} from "../libs/metadata";
|
import {downloadGlyphsMetadata, downloadSpriteMetadata} from "../libs/metadata";
|
||||||
import { emptyStyle, getAccessToken, replaceAccessTokens } from "../libs/style";
|
import { emptyStyle, getAccessToken, replaceAccessTokens } from "../libs/style";
|
||||||
import { undoMessages, redoMessages } from "../libs/diffmessage";
|
import { undoMessages, redoMessages } from "../libs/diffmessage";
|
||||||
import { createStyleStore, type IStyleStore } from "../libs/store/style-store-factory";
|
import { createStyleStore, type IStyleStore } from "../libs/store/style-store-factory";
|
||||||
import { RevisionStore } from "../libs/revisions";
|
import { RevisionStore } from "../libs/revisions";
|
||||||
import { LayerWatcher } from "../libs/layerwatcher";
|
import LayerWatcher from "../libs/layerwatcher";
|
||||||
import tokens from "../config/tokens.json";
|
import tokens from "../config/tokens.json";
|
||||||
import isEqual from "lodash.isequal";
|
import isEqual from "lodash.isequal";
|
||||||
import { type MapOptions } from "maplibre-gl";
|
import { type MapOptions } from "maplibre-gl";
|
||||||
@@ -123,7 +123,7 @@ type AppState = {
|
|||||||
fileHandle: FileSystemFileHandle | null
|
fileHandle: FileSystemFileHandle | null
|
||||||
};
|
};
|
||||||
|
|
||||||
export class App extends React.Component<any, AppState> {
|
export default class App extends React.Component<any, AppState> {
|
||||||
revisionStore: RevisionStore;
|
revisionStore: RevisionStore;
|
||||||
styleStore: IStyleStore | null = null;
|
styleStore: IStyleStore | null = null;
|
||||||
layerWatcher: LayerWatcher;
|
layerWatcher: LayerWatcher;
|
||||||
|
|||||||
+34
-112
@@ -1,23 +1,9 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import { Group, Panel, Separator, useDefaultLayout } from "react-resizable-panels";
|
import ScrollContainer from "./ScrollContainer";
|
||||||
import { ScrollContainer } from "./ScrollContainer";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { IconContext } from "react-icons";
|
import { IconContext } from "react-icons";
|
||||||
|
|
||||||
// Keep these in sync with $layout-list-width/$layout-editor-width in _vars.scss
|
type AppLayoutInternalProps = {
|
||||||
const DEFAULT_LIST_WIDTH = 200;
|
|
||||||
const DEFAULT_DRAWER_WIDTH = 370;
|
|
||||||
const DEFAULT_SIDEBAR_WIDTH = DEFAULT_LIST_WIDTH + DEFAULT_DRAWER_WIDTH;
|
|
||||||
const PANEL_STYLE: React.CSSProperties = { overflow: "hidden" };
|
|
||||||
|
|
||||||
const SIDEBAR_LAYOUT_ID = "maputnik:sidebar-layout";
|
|
||||||
const SIDEBAR_INNER_LAYOUT_ID = "maputnik:sidebar-inner-layout";
|
|
||||||
const SIDEBAR_PANEL_ID = "sidebar";
|
|
||||||
const MAP_PANEL_ID = "map";
|
|
||||||
const LIST_PANEL_ID = "list";
|
|
||||||
const DRAWER_PANEL_ID = "drawer";
|
|
||||||
|
|
||||||
type AppLayoutProps = {
|
|
||||||
toolbar: React.ReactElement
|
toolbar: React.ReactElement
|
||||||
layerList: React.ReactElement
|
layerList: React.ReactElement
|
||||||
layerEditor?: React.ReactElement
|
layerEditor?: React.ReactElement
|
||||||
@@ -25,108 +11,44 @@ type AppLayoutProps = {
|
|||||||
map: React.ReactElement
|
map: React.ReactElement
|
||||||
bottom?: React.ReactElement
|
bottom?: React.ReactElement
|
||||||
modals?: React.ReactNode
|
modals?: React.ReactNode
|
||||||
};
|
} & WithTranslation;
|
||||||
|
|
||||||
export const AppLayout: React.FC<AppLayoutProps> = (props) => {
|
class AppLayoutInternal extends React.Component<AppLayoutInternalProps> {
|
||||||
const { t, i18n } = useTranslation();
|
|
||||||
|
|
||||||
const sidebarLayout = useDefaultLayout({
|
render() {
|
||||||
id: SIDEBAR_LAYOUT_ID,
|
document.body.dir = this.props.i18n.dir();
|
||||||
panelIds: [SIDEBAR_PANEL_ID, MAP_PANEL_ID],
|
|
||||||
});
|
|
||||||
const innerLayout = useDefaultLayout({
|
|
||||||
id: SIDEBAR_INNER_LAYOUT_ID,
|
|
||||||
panelIds: [LIST_PANEL_ID, DRAWER_PANEL_ID],
|
|
||||||
});
|
|
||||||
|
|
||||||
// The bottom panel is position: fixed, so it can't be a flex sibling of the
|
|
||||||
// map panel; it follows the sidebar through this custom property instead.
|
|
||||||
const [sidebarWidth, setSidebarWidth] = useState(DEFAULT_SIDEBAR_WIDTH);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.body.dir = i18n.dir();
|
|
||||||
}, [i18n, i18n.language]);
|
|
||||||
|
|
||||||
return <IconContext.Provider value={{size: "14px"}}>
|
return <IconContext.Provider value={{size: "14px"}}>
|
||||||
<div
|
<div className="maputnik-layout">
|
||||||
className="maputnik-layout"
|
{this.props.toolbar}
|
||||||
style={{"--sidebar-width": `${sidebarWidth}px`} as React.CSSProperties}
|
|
||||||
>
|
|
||||||
{props.toolbar}
|
|
||||||
<div className="maputnik-layout-main">
|
<div className="maputnik-layout-main">
|
||||||
<Group
|
{this.props.codeEditor && <div className="maputnik-layout-code-editor">
|
||||||
className="maputnik-layout-panels"
|
|
||||||
orientation="horizontal"
|
|
||||||
id={SIDEBAR_LAYOUT_ID}
|
|
||||||
defaultLayout={sidebarLayout.defaultLayout}
|
|
||||||
onLayoutChanged={sidebarLayout.onLayoutChanged}
|
|
||||||
>
|
|
||||||
<Panel
|
|
||||||
id={SIDEBAR_PANEL_ID}
|
|
||||||
data-wd-key="sidebar-panel"
|
|
||||||
className={props.codeEditor ? "maputnik-layout-code-editor" : "maputnik-layout-sidebar"}
|
|
||||||
style={PANEL_STYLE}
|
|
||||||
defaultSize={`${DEFAULT_SIDEBAR_WIDTH}px`}
|
|
||||||
minSize="280px"
|
|
||||||
onResize={({inPixels}) => setSidebarWidth(inPixels)}
|
|
||||||
>
|
|
||||||
{props.codeEditor && <ScrollContainer>
|
|
||||||
{props.codeEditor}
|
|
||||||
</ScrollContainer>
|
|
||||||
}
|
|
||||||
{!props.codeEditor && <Group
|
|
||||||
className="maputnik-layout-sidebar-panels"
|
|
||||||
orientation="horizontal"
|
|
||||||
id={SIDEBAR_INNER_LAYOUT_ID}
|
|
||||||
defaultLayout={innerLayout.defaultLayout}
|
|
||||||
onLayoutChanged={innerLayout.onLayoutChanged}
|
|
||||||
>
|
|
||||||
<Panel
|
|
||||||
id={LIST_PANEL_ID}
|
|
||||||
data-wd-key="layer-list-panel"
|
|
||||||
className="maputnik-layout-list"
|
|
||||||
style={PANEL_STYLE}
|
|
||||||
defaultSize={`${DEFAULT_LIST_WIDTH}px`}
|
|
||||||
minSize="100px"
|
|
||||||
>
|
|
||||||
{props.layerList}
|
|
||||||
</Panel>
|
|
||||||
<Separator
|
|
||||||
className="maputnik-layout-resize-handle"
|
|
||||||
data-wd-key="inner-resize-handle"
|
|
||||||
title={t("Drag to resize the layer list")}
|
|
||||||
aria-label={t("Drag to resize the layer list")}
|
|
||||||
/>
|
|
||||||
<Panel
|
|
||||||
id={DRAWER_PANEL_ID}
|
|
||||||
className="maputnik-layout-drawer"
|
|
||||||
style={PANEL_STYLE}
|
|
||||||
defaultSize={`${DEFAULT_DRAWER_WIDTH}px`}
|
|
||||||
minSize="150px"
|
|
||||||
>
|
|
||||||
<ScrollContainer>
|
<ScrollContainer>
|
||||||
{props.layerEditor}
|
{this.props.codeEditor}
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</Panel>
|
|
||||||
</Group>
|
|
||||||
}
|
|
||||||
</Panel>
|
|
||||||
<Separator
|
|
||||||
className="maputnik-layout-resize-handle"
|
|
||||||
data-wd-key="sidebar-resize-handle"
|
|
||||||
title={t("Drag to resize the sidebar")}
|
|
||||||
aria-label={t("Drag to resize the sidebar")}
|
|
||||||
/>
|
|
||||||
<Panel id={MAP_PANEL_ID} className="maputnik-layout-map" style={PANEL_STYLE} minSize="200px">
|
|
||||||
{props.map}
|
|
||||||
</Panel>
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
{props.bottom && <div className="maputnik-layout-bottom">
|
|
||||||
{props.bottom}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{props.modals}
|
{!this.props.codeEditor && <>
|
||||||
|
<div className="maputnik-layout-list">
|
||||||
|
{this.props.layerList}
|
||||||
|
</div>
|
||||||
|
<div className="maputnik-layout-drawer">
|
||||||
|
<ScrollContainer>
|
||||||
|
{this.props.layerEditor}
|
||||||
|
</ScrollContainer>
|
||||||
|
</div>
|
||||||
|
</>}
|
||||||
|
{this.props.map}
|
||||||
|
</div>
|
||||||
|
{this.props.bottom && <div className="maputnik-layout-bottom">
|
||||||
|
{this.props.bottom}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{this.props.modals}
|
||||||
</div>
|
</div>
|
||||||
</IconContext.Provider>;
|
</IconContext.Provider>;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppLayout = withTranslation()(AppLayoutInternal);
|
||||||
|
export default AppLayout;
|
||||||
|
|||||||
@@ -61,4 +61,5 @@ class AppMessagePanelInternal extends React.Component<AppMessagePanelInternalPro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppMessagePanel = withTranslation()(AppMessagePanelInternal);
|
const AppMessagePanel = withTranslation()(AppMessagePanelInternal);
|
||||||
|
export default AppMessagePanel;
|
||||||
|
|||||||
@@ -306,4 +306,5 @@ class AppToolbarInternal extends React.Component<AppToolbarInternalProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppToolbar = withTranslation()(AppToolbarInternal);
|
const AppToolbar = withTranslation()(AppToolbarInternal);
|
||||||
|
export default AppToolbar;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, {type CSSProperties, type PropsWithChildren, type SyntheticEvent} from "react";
|
import React, {type CSSProperties, type PropsWithChildren, type SyntheticEvent} from "react";
|
||||||
import classnames from "classnames";
|
import classnames from "classnames";
|
||||||
import { FieldDocLabel } from "./FieldDocLabel";
|
import FieldDocLabel from "./FieldDocLabel";
|
||||||
import { Doc } from "./Doc";
|
import Doc from "./Doc";
|
||||||
|
|
||||||
export type BlockProps = PropsWithChildren & {
|
export type BlockProps = PropsWithChildren & {
|
||||||
"data-wd-key"?: string
|
"data-wd-key"?: string
|
||||||
@@ -19,7 +19,7 @@ type BlockState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** Wrap a component with a label */
|
/** Wrap a component with a label */
|
||||||
export class Block extends React.Component<BlockProps, BlockState> {
|
export default class Block extends React.Component<BlockProps, BlockState> {
|
||||||
_blockEl: HTMLDivElement | null = null;
|
_blockEl: HTMLDivElement | null = null;
|
||||||
|
|
||||||
constructor (props: BlockProps) {
|
constructor (props: BlockProps) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { InputJson } from "./InputJson";
|
import InputJson from "./InputJson";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { withTranslation, type WithTranslation } from "react-i18next";
|
import { withTranslation, type WithTranslation } from "react-i18next";
|
||||||
import { type StyleSpecification } from "maplibre-gl";
|
import { type StyleSpecification } from "maplibre-gl";
|
||||||
@@ -24,4 +24,6 @@ const CodeEditorInternal: React.FC<CodeEditorProps> = (props) => {
|
|||||||
</>;
|
</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CodeEditor = withTranslation()(CodeEditorInternal);
|
const CodeEditor = withTranslation()(CodeEditorInternal);
|
||||||
|
|
||||||
|
export default CodeEditor;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ type CollapseProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export class Collapse extends React.Component<CollapseProps> {
|
export default class Collapse extends React.Component<CollapseProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
isActive: true
|
isActive: true
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ type CollapserProps = {
|
|||||||
style?: object
|
style?: object
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Collapser extends React.Component<CollapserProps> {
|
export default class Collapser extends React.Component<CollapserProps> {
|
||||||
render() {
|
render() {
|
||||||
const iconStyle = {
|
const iconStyle = {
|
||||||
width: 20,
|
width: 20,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type DocProps = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Doc extends React.Component<DocProps> {
|
export default class Doc extends React.Component<DocProps> {
|
||||||
render () {
|
render () {
|
||||||
const {fieldSpec} = this.props;
|
const {fieldSpec} = this.props;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InputArray, type InputArrayProps } from "./InputArray";
|
import InputArray, { type InputArrayProps } from "./InputArray";
|
||||||
import { Fieldset } from "./Fieldset";
|
import Fieldset from "./Fieldset";
|
||||||
|
|
||||||
type FieldArrayProps = InputArrayProps & {
|
type FieldArrayProps = InputArrayProps & {
|
||||||
name?: string
|
name?: string
|
||||||
@@ -8,10 +8,12 @@ type FieldArrayProps = InputArrayProps & {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldArray: React.FC<FieldArrayProps> = (props) => {
|
const FieldArray: React.FC<FieldArrayProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Fieldset label={props.label} fieldSpec={props.fieldSpec}>
|
<Fieldset label={props.label} fieldSpec={props.fieldSpec}>
|
||||||
<InputArray {...props} />
|
<InputArray {...props} />
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldArray;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputAutocomplete, type InputAutocompleteProps } from "./InputAutocomplete";
|
import InputAutocomplete, { type InputAutocompleteProps } from "./InputAutocomplete";
|
||||||
|
|
||||||
|
|
||||||
type FieldAutocompleteProps = InputAutocompleteProps & {
|
type FieldAutocompleteProps = InputAutocompleteProps & {
|
||||||
@@ -7,10 +7,12 @@ type FieldAutocompleteProps = InputAutocompleteProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldAutocomplete: React.FC<FieldAutocompleteProps> = (props) => {
|
const FieldAutocomplete: React.FC<FieldAutocompleteProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label={props.label}>
|
<Block label={props.label}>
|
||||||
<InputAutocomplete {...props} />
|
<InputAutocomplete {...props} />
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldAutocomplete;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputCheckbox, type InputCheckboxProps } from "./InputCheckbox";
|
import InputCheckbox, {type InputCheckboxProps} from "./InputCheckbox";
|
||||||
|
|
||||||
|
|
||||||
type FieldCheckboxProps = InputCheckboxProps & {
|
type FieldCheckboxProps = InputCheckboxProps & {
|
||||||
@@ -7,10 +7,12 @@ type FieldCheckboxProps = InputCheckboxProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldCheckbox: React.FC<FieldCheckboxProps> = (props) => {
|
const FieldCheckbox: React.FC<FieldCheckboxProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label={props.label}>
|
<Block label={props.label}>
|
||||||
<InputCheckbox {...props} />
|
<InputCheckbox {...props} />
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldCheckbox;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputColor, type InputColorProps } from "./InputColor";
|
import InputColor, {type InputColorProps} from "./InputColor";
|
||||||
|
|
||||||
|
|
||||||
type FieldColorProps = InputColorProps & {
|
type FieldColorProps = InputColorProps & {
|
||||||
@@ -10,10 +10,12 @@ type FieldColorProps = InputColorProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldColor: React.FC<FieldColorProps> = (props) => {
|
const FieldColor: React.FC<FieldColorProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
||||||
<InputColor {...props} />
|
<InputColor {...props} />
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldColor;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
type FieldCommentInternalProps = {
|
type FieldCommentInternalProps = {
|
||||||
@@ -36,4 +36,5 @@ const FieldCommentInternal: React.FC<FieldCommentInternalProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldComment = withTranslation()(FieldCommentInternal);
|
const FieldComment = withTranslation()(FieldCommentInternal);
|
||||||
|
export default FieldComment;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ type FieldDocLabelProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldDocLabel: React.FC<FieldDocLabelProps> = (props) => {
|
const FieldDocLabel: React.FC<FieldDocLabelProps> = (props) => {
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
const onToggleDoc = (state: boolean) => {
|
const onToggleDoc = (state: boolean) => {
|
||||||
@@ -49,3 +49,5 @@ export const FieldDocLabel: React.FC<FieldDocLabelProps> = (props) => {
|
|||||||
}
|
}
|
||||||
return <div />;
|
return <div />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldDocLabel;
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import { InputDynamicArray, type InputDynamicArrayProps } from "./InputDynamicArray";
|
import InputDynamicArray, {type InputDynamicArrayProps} from "./InputDynamicArray";
|
||||||
import { Fieldset } from "./Fieldset";
|
import Fieldset from "./Fieldset";
|
||||||
|
|
||||||
type FieldDynamicArrayProps = InputDynamicArrayProps & {
|
type FieldDynamicArrayProps = InputDynamicArrayProps & {
|
||||||
name?: string;
|
name?: string
|
||||||
fieldSpec?: {
|
|
||||||
value?: string;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldDynamicArray: React.FC<FieldDynamicArrayProps> = (props) => {
|
const FieldDynamicArray: React.FC<FieldDynamicArrayProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Fieldset label={props.label}>
|
<Fieldset label={props.label}>
|
||||||
<InputDynamicArray {...props} />
|
<InputDynamicArray {...props} />
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldDynamicArray;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InputEnum, type InputEnumProps } from "./InputEnum";
|
import InputEnum, {type InputEnumProps} from "./InputEnum";
|
||||||
import { Fieldset } from "./Fieldset";
|
import Fieldset from "./Fieldset";
|
||||||
|
|
||||||
|
|
||||||
type FieldEnumProps = InputEnumProps & {
|
type FieldEnumProps = InputEnumProps & {
|
||||||
@@ -10,10 +10,12 @@ type FieldEnumProps = InputEnumProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldEnum: React.FC<FieldEnumProps> = (props) => {
|
const FieldEnum: React.FC<FieldEnumProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Fieldset label={props.label} fieldSpec={props.fieldSpec}>
|
<Fieldset label={props.label} fieldSpec={props.fieldSpec}>
|
||||||
<InputEnum {...props} />
|
<InputEnum {...props} />
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldEnum;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { SpecProperty } from "./SpecProperty";
|
import SpecProperty from "./_SpecProperty";
|
||||||
import { DataProperty, type Stop } from "./DataProperty";
|
import DataProperty, { type Stop } from "./_DataProperty";
|
||||||
import { ZoomProperty } from "./ZoomProperty";
|
import ZoomProperty from "./_ZoomProperty";
|
||||||
import { ExpressionProperty } from "./ExpressionProperty";
|
import ExpressionProperty from "./_ExpressionProperty";
|
||||||
import {function as styleFunction} from "@maplibre/maplibre-gl-style-spec";
|
import {function as styleFunction} from "@maplibre/maplibre-gl-style-spec";
|
||||||
import {findDefaultFromSpec} from "../libs/spec-helper";
|
import {findDefaultFromSpec} from "../libs/spec-helper";
|
||||||
import { type MappedLayerErrors } from "../libs/definitions";
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
@@ -128,11 +128,17 @@ type FieldFunctionProps = {
|
|||||||
/** Supports displaying spec field for zoom function objects
|
/** Supports displaying spec field for zoom function objects
|
||||||
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
|
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
|
||||||
*/
|
*/
|
||||||
export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
||||||
|
const [dataType, setDataType] = React.useState(
|
||||||
|
getDataType(props.value, props.fieldSpec)
|
||||||
|
);
|
||||||
const [isEditing, setIsEditing] = React.useState(false);
|
const [isEditing, setIsEditing] = React.useState(false);
|
||||||
// Keep the expression editor mounted while typing, but otherwise select the
|
|
||||||
// editor from the current value so a collapsed function never renders as stops.
|
React.useEffect(() => {
|
||||||
const dataType = isEditing ? "expression" : getDataType(props.value, props.fieldSpec);
|
if (!isEditing) {
|
||||||
|
setDataType(getDataType(props.value, props.fieldSpec));
|
||||||
|
}
|
||||||
|
}, [props.value, props.fieldSpec, isEditing]);
|
||||||
|
|
||||||
const getFieldFunctionType = (fieldSpec: any) => {
|
const getFieldFunctionType = (fieldSpec: any) => {
|
||||||
if (fieldSpec.expression.interpolated) {
|
if (fieldSpec.expression.interpolated) {
|
||||||
@@ -167,7 +173,7 @@ export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
|||||||
const deleteExpression = () => {
|
const deleteExpression = () => {
|
||||||
const { fieldSpec, fieldName } = props;
|
const { fieldSpec, fieldName } = props;
|
||||||
props.onChange(fieldName, fieldSpec.default);
|
props.onChange(fieldName, fieldSpec.default);
|
||||||
setIsEditing(false);
|
setDataType("value");
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteStop = (stopIdx: number) => {
|
const deleteStop = (stopIdx: number) => {
|
||||||
@@ -227,10 +233,10 @@ export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
|||||||
type: "identity",
|
type: "identity",
|
||||||
property: value[1],
|
property: value[1],
|
||||||
});
|
});
|
||||||
setIsEditing(false);
|
setDataType("value");
|
||||||
} else if (isLiteralExpression(value)) {
|
} else if (isLiteralExpression(value)) {
|
||||||
props.onChange(fieldName, value[1]);
|
props.onChange(fieldName, value[1]);
|
||||||
setIsEditing(false);
|
setDataType("value");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -396,3 +402,5 @@ export const FieldFunction: React.FC<FieldFunctionProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldFunction;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
|
|
||||||
type FieldIdProps = {
|
type FieldIdProps = {
|
||||||
value: string
|
value: string
|
||||||
@@ -10,7 +10,7 @@ type FieldIdProps = {
|
|||||||
error?: {message: string}
|
error?: {message: string}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldId: React.FC<FieldIdProps> = (props) => {
|
const FieldId: React.FC<FieldIdProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label="ID" fieldSpec={latest.layer.id}
|
<Block label="ID" fieldSpec={latest.layer.id}
|
||||||
data-wd-key={props.wdKey}
|
data-wd-key={props.wdKey}
|
||||||
@@ -24,3 +24,5 @@ export const FieldId: React.FC<FieldIdProps> = (props) => {
|
|||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldId;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { InputJson, type InputJsonProps } from "./InputJson";
|
import InputJson, {type InputJsonProps} from "./InputJson";
|
||||||
|
|
||||||
|
|
||||||
type FieldJsonProps = InputJsonProps & {};
|
type FieldJsonProps = InputJsonProps & {};
|
||||||
|
|
||||||
|
|
||||||
export const FieldJson: React.FC<FieldJsonProps> = (props) => {
|
const FieldJson: React.FC<FieldJsonProps> = (props) => {
|
||||||
return <InputJson {...props} />;
|
return <InputJson {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldJson;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputNumber } from "./InputNumber";
|
import InputNumber from "./InputNumber";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
type FieldMaxZoomInternalProps = {
|
type FieldMaxZoomInternalProps = {
|
||||||
@@ -31,4 +31,5 @@ const FieldMaxZoomInternal: React.FC<FieldMaxZoomInternalProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldMaxZoom = withTranslation()(FieldMaxZoomInternal);
|
const FieldMaxZoom = withTranslation()(FieldMaxZoomInternal);
|
||||||
|
export default FieldMaxZoom;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputNumber } from "./InputNumber";
|
import InputNumber from "./InputNumber";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
type FieldMinZoomInternalProps = {
|
type FieldMinZoomInternalProps = {
|
||||||
@@ -31,4 +31,5 @@ const FieldMinZoomInternal: React.FC<FieldMinZoomInternalProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldMinZoom = withTranslation()(FieldMinZoomInternal);
|
const FieldMinZoom = withTranslation()(FieldMinZoomInternal);
|
||||||
|
export default FieldMinZoom;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InputMultiInput, type InputMultiInputProps } from "./InputMultiInput";
|
import InputMultiInput, {type InputMultiInputProps} from "./InputMultiInput";
|
||||||
import { Fieldset } from "./Fieldset";
|
import Fieldset from "./Fieldset";
|
||||||
|
|
||||||
|
|
||||||
type FieldMultiInputProps = InputMultiInputProps & {
|
type FieldMultiInputProps = InputMultiInputProps & {
|
||||||
@@ -7,10 +7,12 @@ type FieldMultiInputProps = InputMultiInputProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldMultiInput: React.FC<FieldMultiInputProps> = (props) => {
|
const FieldMultiInput: React.FC<FieldMultiInputProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Fieldset label={props.label}>
|
<Fieldset label={props.label}>
|
||||||
<InputMultiInput {...props} />
|
<InputMultiInput {...props} />
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldMultiInput;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InputNumber, type InputNumberProps } from "./InputNumber";
|
import InputNumber, {type InputNumberProps} from "./InputNumber";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
|
|
||||||
|
|
||||||
type FieldNumberProps = InputNumberProps & {
|
type FieldNumberProps = InputNumberProps & {
|
||||||
@@ -10,10 +10,12 @@ type FieldNumberProps = InputNumberProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldNumber: React.FC<FieldNumberProps> = (props) => {
|
const FieldNumber: React.FC<FieldNumberProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
||||||
<InputNumber {...props} />
|
<InputNumber {...props} />
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldNumber;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputSelect, type InputSelectProps } from "./InputSelect";
|
import InputSelect, {type InputSelectProps} from "./InputSelect";
|
||||||
|
|
||||||
|
|
||||||
type FieldSelectProps = InputSelectProps & {
|
type FieldSelectProps = InputSelectProps & {
|
||||||
@@ -10,10 +10,12 @@ type FieldSelectProps = InputSelectProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldSelect: React.FC<FieldSelectProps> = (props) => {
|
const FieldSelect: React.FC<FieldSelectProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
||||||
<InputSelect {...props} />
|
<InputSelect {...props} />
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldSelect;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputAutocomplete } from "./InputAutocomplete";
|
import InputAutocomplete from "./InputAutocomplete";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
type FieldSourceInternalProps = {
|
type FieldSourceInternalProps = {
|
||||||
@@ -38,4 +38,5 @@ const FieldSourceInternal: React.FC<FieldSourceInternalProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldSource = withTranslation()(FieldSourceInternal);
|
const FieldSource = withTranslation()(FieldSourceInternal);
|
||||||
|
export default FieldSource;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import {latest} from "@maplibre/maplibre-gl-style-spec";
|
import {latest} from "@maplibre/maplibre-gl-style-spec";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputAutocomplete } from "./InputAutocomplete";
|
import InputAutocomplete from "./InputAutocomplete";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
type FieldSourceLayerInternalProps = {
|
type FieldSourceLayerInternalProps = {
|
||||||
@@ -35,4 +35,5 @@ const FieldSourceLayerInternal: React.FC<FieldSourceLayerInternalProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldSourceLayer = withTranslation()(FieldSourceLayerInternal);
|
const FieldSourceLayer = withTranslation()(FieldSourceLayerInternal);
|
||||||
|
export default FieldSourceLayer;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Block, type BlockProps } from "./Block";
|
import Block, { type BlockProps } from "./Block";
|
||||||
import { InputSpec, type FieldSpecType, type InputSpecProps } from "./InputSpec";
|
import InputSpec, { type FieldSpecType, type InputSpecProps } from "./InputSpec";
|
||||||
import { Fieldset, type FieldsetProps } from "./Fieldset";
|
import Fieldset, { type FieldsetProps } from "./Fieldset";
|
||||||
|
|
||||||
function getElementFromType(fieldSpec: { type?: FieldSpecType, values?: unknown[] }): typeof Fieldset | typeof Block {
|
function getElementFromType(fieldSpec: { type?: FieldSpecType, values?: unknown[] }): typeof Fieldset | typeof Block {
|
||||||
switch(fieldSpec.type) {
|
switch(fieldSpec.type) {
|
||||||
@@ -36,7 +36,7 @@ function getElementFromType(fieldSpec: { type?: FieldSpecType, values?: unknown[
|
|||||||
|
|
||||||
export type FieldSpecProps = InputSpecProps & BlockProps & FieldsetProps;
|
export type FieldSpecProps = InputSpecProps & BlockProps & FieldsetProps;
|
||||||
|
|
||||||
export const FieldSpec: React.FC<FieldSpecProps> = (props) => {
|
const FieldSpec: React.FC<FieldSpecProps> = (props) => {
|
||||||
const TypeBlock = getElementFromType(props.fieldSpec!);
|
const TypeBlock = getElementFromType(props.fieldSpec!);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -45,3 +45,5 @@ export const FieldSpec: React.FC<FieldSpecProps> = (props) => {
|
|||||||
</TypeBlock>
|
</TypeBlock>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldSpec;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputString, type InputStringProps } from "./InputString";
|
import InputString, {type InputStringProps} from "./InputString";
|
||||||
|
|
||||||
type FieldStringProps = InputStringProps & {
|
type FieldStringProps = InputStringProps & {
|
||||||
name?: string
|
name?: string
|
||||||
@@ -9,10 +9,12 @@ type FieldStringProps = InputStringProps & {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldString: React.FC<FieldStringProps> = (props) => {
|
const FieldString: React.FC<FieldStringProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
||||||
<InputString {...props} />
|
<InputString {...props} />
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldString;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {v8} from "@maplibre/maplibre-gl-style-spec";
|
import {v8} from "@maplibre/maplibre-gl-style-spec";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputSelect } from "./InputSelect";
|
import InputSelect from "./InputSelect";
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { startCase } from "lodash";
|
import { startCase } from "lodash";
|
||||||
|
|
||||||
@@ -43,4 +43,5 @@ const FieldTypeInternal: React.FC<FieldTypeInternalProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldType = withTranslation()(FieldTypeInternal);
|
const FieldType = withTranslation()(FieldTypeInternal);
|
||||||
|
export default FieldType;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InputUrl, type FieldUrlProps as InputUrlProps } from "./InputUrl";
|
import InputUrl, {type FieldUrlProps as InputUrlProps} from "./InputUrl";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
|
|
||||||
|
|
||||||
type FieldUrlProps = InputUrlProps & {
|
type FieldUrlProps = InputUrlProps & {
|
||||||
@@ -10,10 +10,12 @@ type FieldUrlProps = InputUrlProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FieldUrl: React.FC<FieldUrlProps> = (props) => {
|
const FieldUrl: React.FC<FieldUrlProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
<Block label={props.label} fieldSpec={props.fieldSpec}>
|
||||||
<InputUrl {...props} />
|
<InputUrl {...props} />
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default FieldUrl;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { type PropsWithChildren, type ReactElement } from "react";
|
import React, { type PropsWithChildren, type ReactElement } from "react";
|
||||||
import classnames from "classnames";
|
import classnames from "classnames";
|
||||||
import { FieldDocLabel } from "./FieldDocLabel";
|
import FieldDocLabel from "./FieldDocLabel";
|
||||||
import { Doc } from "./Doc";
|
import Doc from "./Doc";
|
||||||
import { generateUniqueId } from "../libs/document-uid";
|
import generateUniqueId from "../libs/document-uid";
|
||||||
|
|
||||||
export type FieldsetProps = PropsWithChildren & {
|
export type FieldsetProps = PropsWithChildren & {
|
||||||
label?: string,
|
label?: string,
|
||||||
@@ -12,7 +12,7 @@ export type FieldsetProps = PropsWithChildren & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const Fieldset: React.FC<FieldsetProps> = (props) => {
|
const Fieldset: React.FC<FieldsetProps> = (props) => {
|
||||||
const [showDoc, setShowDoc] = React.useState(false);
|
const [showDoc, setShowDoc] = React.useState(false);
|
||||||
const labelId = React.useRef(generateUniqueId("fieldset_label_"));
|
const labelId = React.useRef(generateUniqueId("fieldset_label_"));
|
||||||
|
|
||||||
@@ -49,3 +49,5 @@ export const Fieldset: React.FC<FieldsetProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default Fieldset;
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import {migrate, convertFilter} from "@maplibre/maplibre-gl-style-spec";
|
|||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
|
|
||||||
import {combiningFilterOps} from "../libs/filterops";
|
import {combiningFilterOps} from "../libs/filterops";
|
||||||
import { InputSelect } from "./InputSelect";
|
import InputSelect from "./InputSelect";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { SingleFilterEditor } from "./SingleFilterEditor";
|
import SingleFilterEditor from "./SingleFilterEditor";
|
||||||
import { FilterEditorBlock } from "./FilterEditorBlock";
|
import FilterEditorBlock from "./FilterEditorBlock";
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import { Doc } from "./Doc";
|
import Doc from "./Doc";
|
||||||
import { ExpressionProperty } from "./ExpressionProperty";
|
import ExpressionProperty from "./_ExpressionProperty";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import type { MappedLayerErrors, StyleSpecificationWithId } from "../libs/definitions";
|
import type { MappedLayerErrors, StyleSpecificationWithId } from "../libs/definitions";
|
||||||
|
|
||||||
@@ -318,4 +318,5 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FilterEditor = withTranslation()(FilterEditorInternal);
|
const FilterEditor = withTranslation()(FilterEditorInternal);
|
||||||
|
export default FilterEditor;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { type PropsWithChildren } from "react";
|
import React, { type PropsWithChildren } from "react";
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import {MdDelete} from "react-icons/md";
|
import {MdDelete} from "react-icons/md";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
@@ -27,4 +27,5 @@ class FilterEditorBlockInternal extends React.Component<FilterEditorBlockInterna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FilterEditorBlock = withTranslation()(FilterEditorBlockInternal);
|
const FilterEditorBlock = withTranslation()(FilterEditorBlockInternal);
|
||||||
|
export default FilterEditorBlock;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type IconLayerProps = {
|
|||||||
className?: string
|
className?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IconLayer: React.FC<IconLayerProps> = (props) => {
|
const IconLayer: React.FC<IconLayerProps> = (props) => {
|
||||||
const iconProps = { style: props.style };
|
const iconProps = { style: props.style };
|
||||||
switch(props.type) {
|
switch(props.type) {
|
||||||
case "fill-extrusion": return <IoMdCube {...iconProps} />;
|
case "fill-extrusion": return <IoMdCube {...iconProps} />;
|
||||||
@@ -29,3 +29,5 @@ export const IconLayer: React.FC<IconLayerProps> = (props) => {
|
|||||||
default: return <MdPriorityHigh {...iconProps} />;
|
default: return <MdPriorityHigh {...iconProps} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default IconLayer;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
import { InputNumber } from "./InputNumber";
|
import InputNumber from "./InputNumber";
|
||||||
|
|
||||||
export type InputArrayProps = {
|
export type InputArrayProps = {
|
||||||
value: (string | number | undefined)[]
|
value: (string | number | undefined)[]
|
||||||
@@ -17,7 +17,7 @@ type InputArrayState = {
|
|||||||
initialPropsValue: unknown[]
|
initialPropsValue: unknown[]
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputArray extends React.Component<InputArrayProps, InputArrayState> {
|
export default class InputArray extends React.Component<InputArrayProps, InputArrayState> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
value: [],
|
value: [],
|
||||||
default: [],
|
default: [],
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export type InputAutocompleteProps = {
|
|||||||
"aria-label"?: string
|
"aria-label"?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export function InputAutocomplete({
|
export default function InputAutocomplete({
|
||||||
value,
|
value,
|
||||||
options = [],
|
options = [],
|
||||||
onChange = () => {},
|
onChange = () => {},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type InputButtonProps = {
|
|||||||
title?: string
|
title?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputButton extends React.Component<InputButtonProps> {
|
export default class InputButton extends React.Component<InputButtonProps> {
|
||||||
render() {
|
render() {
|
||||||
return <button
|
return <button
|
||||||
id={this.props.id}
|
id={this.props.id}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export type InputCheckboxProps = {
|
|||||||
onChange(...args: unknown[]): unknown
|
onChange(...args: unknown[]): unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputCheckbox extends React.Component<InputCheckboxProps> {
|
export default class InputCheckbox extends React.Component<InputCheckboxProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
value: false,
|
value: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Color from "color";
|
import Color from "color";
|
||||||
import {ChromePicker, type ColorResult} from "react-color";
|
import ChromePicker from "react-color/lib/components/chrome/Chrome";
|
||||||
|
import {type ColorResult} from "react-color";
|
||||||
import lodash from "lodash";
|
import lodash from "lodash";
|
||||||
|
|
||||||
function formatColor(color: ColorResult): string {
|
function formatColor(color: ColorResult): string {
|
||||||
@@ -19,7 +20,7 @@ export type InputColorProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*** Number fields with support for min, max and units and documentation*/
|
/*** Number fields with support for min, max and units and documentation*/
|
||||||
export class InputColor extends React.Component<InputColorProps> {
|
export default class InputColor extends React.Component<InputColorProps> {
|
||||||
state = {
|
state = {
|
||||||
pickerOpened: false
|
pickerOpened: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import capitalize from "lodash.capitalize";
|
|||||||
import {MdDelete} from "react-icons/md";
|
import {MdDelete} from "react-icons/md";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
import { InputNumber } from "./InputNumber";
|
import InputNumber from "./InputNumber";
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import { FieldDocLabel } from "./FieldDocLabel";
|
import FieldDocLabel from "./FieldDocLabel";
|
||||||
import { InputEnum } from "./InputEnum";
|
import InputEnum from "./InputEnum";
|
||||||
import { InputUrl } from "./InputUrl";
|
import InputUrl from "./InputUrl";
|
||||||
import { InputColor } from "./InputColor";
|
import InputColor from "./InputColor";
|
||||||
|
|
||||||
|
|
||||||
export type InputDynamicArrayProps = {
|
export type InputDynamicArrayProps = {
|
||||||
@@ -141,7 +141,9 @@ class InputDynamicArrayInternal extends React.Component<InputDynamicArrayInterna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InputDynamicArray = withTranslation()(InputDynamicArrayInternal);
|
const InputDynamicArray = withTranslation()(InputDynamicArrayInternal);
|
||||||
|
export default InputDynamicArray;
|
||||||
|
|
||||||
type DeleteValueInputButtonProps = {
|
type DeleteValueInputButtonProps = {
|
||||||
onClick?(...args: unknown[]): unknown
|
onClick?(...args: unknown[]): unknown
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { InputSelect } from "./InputSelect";
|
import InputSelect from "./InputSelect";
|
||||||
import { InputMultiInput } from "./InputMultiInput";
|
import InputMultiInput from "./InputMultiInput";
|
||||||
|
|
||||||
|
|
||||||
function optionsLabelLength(options: any[]) {
|
function optionsLabelLength(options: any[]) {
|
||||||
@@ -25,7 +25,7 @@ export type InputEnumProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export class InputEnum extends React.Component<InputEnumProps> {
|
export default class InputEnum extends React.Component<InputEnumProps> {
|
||||||
render() {
|
render() {
|
||||||
const {options, value, onChange, name, label} = this.props;
|
const {options, value, onChange, name, label} = this.props;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { InputAutocomplete } from "./InputAutocomplete";
|
import InputAutocomplete from "./InputAutocomplete";
|
||||||
|
|
||||||
export type InputFontProps = {
|
export type InputFontProps = {
|
||||||
name: string
|
name: string
|
||||||
@@ -11,7 +11,7 @@ export type InputFontProps = {
|
|||||||
"aria-label"?: string
|
"aria-label"?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputFont extends React.Component<InputFontProps> {
|
export default class InputFont extends React.Component<InputFontProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
fonts: []
|
fonts: []
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -135,4 +135,5 @@ class InputJsonInternal extends React.Component<InputJsonInternalProps, InputJso
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InputJson = withTranslation()(InputJsonInternal);
|
const InputJson = withTranslation()(InputJsonInternal);
|
||||||
|
export default InputJson;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export type InputMultiInputProps = {
|
|||||||
"aria-label"?: string
|
"aria-label"?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputMultiInput extends React.Component<InputMultiInputProps> {
|
export default class InputMultiInput extends React.Component<InputMultiInputProps> {
|
||||||
render() {
|
render() {
|
||||||
let options = this.props.options;
|
let options = this.props.options;
|
||||||
if(options.length > 0 && !Array.isArray(options[0])) {
|
if(options.length > 0 && !Array.isArray(options[0])) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { type BaseSyntheticEvent } from "react";
|
import React, { type BaseSyntheticEvent } from "react";
|
||||||
import { generateUniqueId } from "../libs/document-uid";
|
import generateUniqueId from "../libs/document-uid";
|
||||||
|
|
||||||
export type InputNumberProps = {
|
export type InputNumberProps = {
|
||||||
value?: number
|
value?: number
|
||||||
@@ -25,7 +25,7 @@ type InputNumberState = {
|
|||||||
dirtyValue?: number | string | undefined
|
dirtyValue?: number | string | undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputNumber extends React.Component<InputNumberProps, InputNumberState> {
|
export default class InputNumber extends React.Component<InputNumberProps, InputNumberState> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
rangeStep: 1
|
rangeStep: 1
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export type InputSelectProps = {
|
|||||||
"aria-label"?: string
|
"aria-label"?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputSelect extends React.Component<InputSelectProps> {
|
export default class InputSelect extends React.Component<InputSelectProps> {
|
||||||
render() {
|
render() {
|
||||||
let options = this.props.options;
|
let options = this.props.options;
|
||||||
if(options.length > 0 && !Array.isArray(options[0])) {
|
if(options.length > 0 && !Array.isArray(options[0])) {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
import { InputColor, type InputColorProps } from "./InputColor";
|
import InputColor, { type InputColorProps } from "./InputColor";
|
||||||
import { InputNumber, type InputNumberProps } from "./InputNumber";
|
import InputNumber, { type InputNumberProps } from "./InputNumber";
|
||||||
import { InputCheckbox, type InputCheckboxProps } from "./InputCheckbox";
|
import InputCheckbox, { type InputCheckboxProps } from "./InputCheckbox";
|
||||||
import { InputString, type InputStringProps } from "./InputString";
|
import InputString, { type InputStringProps } from "./InputString";
|
||||||
import { InputArray, type InputArrayProps } from "./InputArray";
|
import InputArray, { type InputArrayProps } from "./InputArray";
|
||||||
import { InputDynamicArray, type InputDynamicArrayProps } from "./InputDynamicArray";
|
import InputDynamicArray, { type InputDynamicArrayProps } from "./InputDynamicArray";
|
||||||
import { InputFont, type InputFontProps } from "./InputFont";
|
import InputFont, { type InputFontProps } from "./InputFont";
|
||||||
import { InputAutocomplete, type InputAutocompleteProps } from "./InputAutocomplete";
|
import InputAutocomplete, { type InputAutocompleteProps } from "./InputAutocomplete";
|
||||||
import { InputEnum, type InputEnumProps } from "./InputEnum";
|
import InputEnum, { type InputEnumProps } from "./InputEnum";
|
||||||
import capitalize from "lodash.capitalize";
|
import capitalize from "lodash.capitalize";
|
||||||
|
|
||||||
const iconProperties = ["background-pattern", "fill-pattern", "line-pattern", "fill-extrusion-pattern", "icon-image"];
|
const iconProperties = ["background-pattern", "fill-pattern", "line-pattern", "fill-extrusion-pattern", "icon-image"];
|
||||||
@@ -38,7 +38,7 @@ export type InputSpecProps = {
|
|||||||
/** Display any field from the Maplibre GL style spec and
|
/** Display any field from the Maplibre GL style spec and
|
||||||
* choose the correct field component based on the @{fieldSpec}
|
* choose the correct field component based on the @{fieldSpec}
|
||||||
* to display @{value}. */
|
* to display @{value}. */
|
||||||
export class InputSpec extends React.Component<InputSpecProps> {
|
export default class InputSpec extends React.Component<InputSpecProps> {
|
||||||
|
|
||||||
childNodes() {
|
childNodes() {
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type InputStringState = {
|
|||||||
value?: string
|
value?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export class InputString extends React.Component<InputStringProps, InputStringState> {
|
export default class InputString extends React.Component<InputStringProps, InputStringState> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onInput: () => {},
|
onInput: () => {},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { type JSX } from "react";
|
import React, { type JSX } from "react";
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
import { SmallError } from "./SmallError";
|
import SmallError from "./SmallError";
|
||||||
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { type TFunction } from "i18next";
|
import { type TFunction } from "i18next";
|
||||||
import { ErrorType, validate } from "../libs/urlopen";
|
import { ErrorType, validate } from "../libs/urlopen";
|
||||||
@@ -91,4 +91,5 @@ class InputUrlInternal extends React.Component<InputUrlInternalProps, InputUrlSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InputUrl = withTranslation()(InputUrlInternal);
|
const InputUrl = withTranslation()(InputUrlInternal);
|
||||||
|
export default InputUrl;
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import { IconContext } from "react-icons";
|
|||||||
import { type BackgroundLayerSpecification, type LayerSpecification, type SourceSpecification } from "maplibre-gl";
|
import { type BackgroundLayerSpecification, type LayerSpecification, type SourceSpecification } from "maplibre-gl";
|
||||||
import { v8 } from "@maplibre/maplibre-gl-style-spec";
|
import { v8 } from "@maplibre/maplibre-gl-style-spec";
|
||||||
|
|
||||||
import { FieldJson } from "./FieldJson";
|
import FieldJson from "./FieldJson";
|
||||||
import { FilterEditor } from "./FilterEditor";
|
import FilterEditor from "./FilterEditor";
|
||||||
import { PropertyGroup } from "./PropertyGroup";
|
import PropertyGroup from "./PropertyGroup";
|
||||||
import { LayerEditorGroup } from "./LayerEditorGroup";
|
import LayerEditorGroup from "./LayerEditorGroup";
|
||||||
import { FieldType } from "./FieldType";
|
import FieldType from "./FieldType";
|
||||||
import { FieldId } from "./FieldId";
|
import FieldId from "./FieldId";
|
||||||
import { FieldMinZoom } from "./FieldMinZoom";
|
import FieldMinZoom from "./FieldMinZoom";
|
||||||
import { FieldMaxZoom } from "./FieldMaxZoom";
|
import FieldMaxZoom from "./FieldMaxZoom";
|
||||||
import { FieldComment } from "./FieldComment";
|
import FieldComment from "./FieldComment";
|
||||||
import { FieldSource } from "./FieldSource";
|
import FieldSource from "./FieldSource";
|
||||||
import { FieldSourceLayer } from "./FieldSourceLayer";
|
import FieldSourceLayer from "./FieldSourceLayer";
|
||||||
import { changeType, changeProperty } from "../libs/layer";
|
import { changeType, changeProperty } from "../libs/layer";
|
||||||
import { formatLayerId } from "../libs/format";
|
import { formatLayerId } from "../libs/format";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
@@ -74,20 +74,20 @@ function getLayoutForType(type: LayerSpecification["type"], t: TFunction): Maput
|
|||||||
return getLayoutForSymbolType(t);
|
return getLayoutForSymbolType(t);
|
||||||
}
|
}
|
||||||
const groups: MaputnikLayoutGroup[] = [];
|
const groups: MaputnikLayoutGroup[] = [];
|
||||||
if (Object.keys(v8["paint_" + type as keyof typeof v8]).length > 0) {
|
if (Object.keys(v8["paint_" + type]).length > 0) {
|
||||||
groups.push({
|
groups.push({
|
||||||
title: t("Paint properties"),
|
title: t("Paint properties"),
|
||||||
id: "Paint_properties",
|
id: "Paint_properties",
|
||||||
type: "properties",
|
type: "properties",
|
||||||
fields: Object.keys(v8["paint_" + type as keyof typeof v8]),
|
fields: Object.keys(v8["paint_" + type]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (Object.keys(v8["layout_" + type as keyof typeof v8]).length > 0) {
|
if (Object.keys(v8["layout_" + type]).length > 0) {
|
||||||
groups.push({
|
groups.push({
|
||||||
title: t("Layout properties"),
|
title: t("Layout properties"),
|
||||||
id: "Layout_properties",
|
id: "Layout_properties",
|
||||||
type: "properties",
|
type: "properties",
|
||||||
fields: Object.keys(v8["layout_" + type as keyof typeof v8])
|
fields: Object.keys(v8["layout_" + type])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return groups;
|
return groups;
|
||||||
@@ -420,4 +420,5 @@ class LayerEditorInternal extends React.Component<LayerEditorInternalProps, Laye
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LayerEditor = withTranslation()(LayerEditorInternal);
|
const LayerEditor = withTranslation()(LayerEditorInternal);
|
||||||
|
export default LayerEditor;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type LayerEditorGroupProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export class LayerEditorGroup extends React.Component<LayerEditorGroupProps> {
|
export default class LayerEditorGroup extends React.Component<LayerEditorGroupProps> {
|
||||||
render() {
|
render() {
|
||||||
return <AccordionItem uuid={this.props.id}>
|
return <AccordionItem uuid={this.props.id}>
|
||||||
<AccordionItemHeading className="maputnik-layer-editor-group"
|
<AccordionItemHeading className="maputnik-layer-editor-group"
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import {
|
|||||||
verticalListSortingStrategy,
|
verticalListSortingStrategy,
|
||||||
} from "@dnd-kit/sortable";
|
} from "@dnd-kit/sortable";
|
||||||
|
|
||||||
import { LayerListGroup } from "./LayerListGroup";
|
import LayerListGroup from "./LayerListGroup";
|
||||||
import { LayerListItem } from "./LayerListItem";
|
import LayerListItem from "./LayerListItem";
|
||||||
import { ModalAdd } from "./modals/ModalAdd";
|
import ModalAdd from "./modals/ModalAdd";
|
||||||
|
|
||||||
import type {LayerSpecification, SourceSpecification} from "maplibre-gl";
|
import type {LayerSpecification, SourceSpecification} from "maplibre-gl";
|
||||||
import { generateUniqueId } from "../libs/document-uid";
|
import generateUniqueId from "../libs/document-uid";
|
||||||
import { findClosestCommonPrefix, layerPrefix } from "../libs/layer";
|
import { findClosestCommonPrefix, layerPrefix } from "../libs/layer";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { type MappedError, type OnMoveLayerCallback } from "../libs/definitions";
|
import { type MappedError, type OnMoveLayerCallback } from "../libs/definitions";
|
||||||
@@ -106,13 +106,13 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
groupedLayers(layers = this.props.layers): (LayerSpecification & {key: string})[][] {
|
groupedLayers(): (LayerSpecification & {key: string})[][] {
|
||||||
const groups = [];
|
const groups = [];
|
||||||
const layerIdCount = new Map();
|
const layerIdCount = new Map();
|
||||||
|
|
||||||
for (let i = 0; i < layers.length; i++) {
|
for (let i = 0; i < this.props.layers.length; i++) {
|
||||||
const origLayer = layers[i];
|
const origLayer = this.props.layers[i];
|
||||||
const previousLayer = layers[i-1];
|
const previousLayer = this.props.layers[i-1];
|
||||||
layerIdCount.set(origLayer.id,
|
layerIdCount.set(origLayer.id,
|
||||||
layerIdCount.has(origLayer.id) ? layerIdCount.get(origLayer.id) + 1 : 0
|
layerIdCount.has(origLayer.id) ? layerIdCount.get(origLayer.id) + 1 : 0
|
||||||
);
|
);
|
||||||
@@ -192,39 +192,7 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
|||||||
return propsChanged;
|
return propsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Preserves group state through surviving layer IDs when group indices change.
|
|
||||||
* If deletion joins groups, the result stays expanded when either group was.
|
|
||||||
*/
|
|
||||||
private preserveLayerGroupState(previousLayers: LayerSpecification[]) {
|
|
||||||
if (previousLayers === this.props.layers) return;
|
|
||||||
|
|
||||||
const collapsedByLayer = new Map<string, boolean>();
|
|
||||||
let idx = 0;
|
|
||||||
for (const layers of this.groupedLayers(previousLayers)) {
|
|
||||||
const collapsed = this.isCollapsed(layerPrefix(layers[0].id), idx);
|
|
||||||
for (const layer of layers) {
|
|
||||||
collapsedByLayer.set(layer.id, collapsed);
|
|
||||||
}
|
|
||||||
idx += layers.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
const collapsedGroups: {[key: string]: boolean} = {};
|
|
||||||
idx = 0;
|
|
||||||
for (const layers of this.groupedLayers()) {
|
|
||||||
const lookupKey = [layerPrefix(layers[0].id), idx].join("-");
|
|
||||||
collapsedGroups[lookupKey] = layers.every(layer => collapsedByLayer.get(layer.id) !== false);
|
|
||||||
idx += layers.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lodash.isEqual(collapsedGroups, this.state.collapsedGroups)) {
|
|
||||||
this.setState({ collapsedGroups });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps: LayerListContainerProps) {
|
componentDidUpdate (prevProps: LayerListContainerProps) {
|
||||||
this.preserveLayerGroupState(prevProps.layers);
|
|
||||||
|
|
||||||
if (prevProps.selectedLayerIndex !== this.props.selectedLayerIndex) {
|
if (prevProps.selectedLayerIndex !== this.props.selectedLayerIndex) {
|
||||||
const selectedItemNode = this.selectedItemRef.current;
|
const selectedItemNode = this.selectedItemRef.current;
|
||||||
if (selectedItemNode && selectedItemNode.node) {
|
if (selectedItemNode && selectedItemNode.node) {
|
||||||
@@ -368,7 +336,7 @@ type LayerListProps = LayerListContainerProps & {
|
|||||||
onMoveLayer: OnMoveLayerCallback
|
onMoveLayer: OnMoveLayerCallback
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LayerList: React.FC<LayerListProps> = (props) => {
|
const LayerList: React.FC<LayerListProps> = (props) => {
|
||||||
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }));
|
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }));
|
||||||
|
|
||||||
const handleDragEnd = (event: DragEndEvent) => {
|
const handleDragEnd = (event: DragEndEvent) => {
|
||||||
@@ -393,3 +361,5 @@ export const LayerList: React.FC<LayerListProps> = (props) => {
|
|||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default LayerList;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Collapser } from "./Collapser";
|
import Collapser from "./Collapser";
|
||||||
|
|
||||||
type LayerListGroupProps = {
|
type LayerListGroupProps = {
|
||||||
title: string
|
title: string
|
||||||
@@ -9,7 +9,7 @@ type LayerListGroupProps = {
|
|||||||
"aria-controls"?: string
|
"aria-controls"?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export class LayerListGroup extends React.Component<LayerListGroupProps> {
|
export default class LayerListGroup extends React.Component<LayerListGroupProps> {
|
||||||
render() {
|
render() {
|
||||||
return <li className="maputnik-layer-list-group">
|
return <li className="maputnik-layer-list-group">
|
||||||
<div className="maputnik-layer-list-group-header"
|
<div className="maputnik-layer-list-group-header"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { IconContext } from "react-icons";
|
|||||||
import { useSortable } from "@dnd-kit/sortable";
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
|
|
||||||
import { IconLayer } from "./IconLayer";
|
import IconLayer from "./IconLayer";
|
||||||
import type { VisibilitySpecification } from "maplibre-gl";
|
import type { VisibilitySpecification } from "maplibre-gl";
|
||||||
|
|
||||||
|
|
||||||
@@ -32,8 +32,6 @@ const DraggableLabel: React.FC<DraggableLabelProps> = (props) => {
|
|||||||
|
|
||||||
type IconActionProps = {
|
type IconActionProps = {
|
||||||
action: string
|
action: string
|
||||||
/** Tooltip text, for buttons whose action reads differently from their icon. */
|
|
||||||
title?: string
|
|
||||||
onClick(...args: unknown[]): unknown
|
onClick(...args: unknown[]): unknown
|
||||||
wdKey?: string
|
wdKey?: string
|
||||||
classBlockName?: string
|
classBlockName?: string
|
||||||
@@ -64,7 +62,7 @@ class IconAction extends React.Component<IconActionProps> {
|
|||||||
|
|
||||||
return <button
|
return <button
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title={this.props.title ?? this.props.action}
|
title={this.props.action}
|
||||||
className={`maputnik-layer-list-icon-action ${classAdditions}`}
|
className={`maputnik-layer-list-icon-action ${classAdditions}`}
|
||||||
data-wd-key={this.props.wdKey}
|
data-wd-key={this.props.wdKey}
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
@@ -89,7 +87,7 @@ type LayerListItemProps = {
|
|||||||
onLayerVisibilityToggle?(...args: unknown[]): unknown
|
onLayerVisibilityToggle?(...args: unknown[]): unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>((props, ref) => {
|
const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>((props, ref) => {
|
||||||
const {
|
const {
|
||||||
isSelected = false,
|
isSelected = false,
|
||||||
visibility = "visible",
|
visibility = "visible",
|
||||||
@@ -113,12 +111,7 @@ export const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>
|
|||||||
opacity: isDragging ? 0.5 : 1,
|
opacity: isDragging ? 0.5 : 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The icon and the CSS modifier describe the layer's current visibility,
|
const visibilityAction = visibility === "visible" ? "show" : "hide";
|
||||||
// while the tooltip has to describe what clicking the button does, which is
|
|
||||||
// the opposite of it.
|
|
||||||
const isVisible = visibility === "visible";
|
|
||||||
const visibilityAction = isVisible ? "show" : "hide";
|
|
||||||
const visibilityTitle = isVisible ? "hide" : "show";
|
|
||||||
|
|
||||||
// Cast ref to MutableRefObject since we know from the codebase that's what's always passed
|
// Cast ref to MutableRefObject since we know from the codebase that's what's always passed
|
||||||
const refObject = ref as React.MutableRefObject<HTMLLIElement | null> | null;
|
const refObject = ref as React.MutableRefObject<HTMLLIElement | null> | null;
|
||||||
@@ -162,7 +155,6 @@ export const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>
|
|||||||
<IconAction
|
<IconAction
|
||||||
wdKey={"layer-list-item:" + props.layerId + ":toggle-visibility"}
|
wdKey={"layer-list-item:" + props.layerId + ":toggle-visibility"}
|
||||||
action={visibilityAction}
|
action={visibilityAction}
|
||||||
title={visibilityTitle}
|
|
||||||
classBlockName="visibility"
|
classBlockName="visibility"
|
||||||
classBlockModifier={visibilityAction}
|
classBlockModifier={visibilityAction}
|
||||||
onClick={_e => onLayerVisibilityToggle!(props.layerIndex)}
|
onClick={_e => onLayerVisibilityToggle!(props.layerIndex)}
|
||||||
@@ -170,3 +162,5 @@ export const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>
|
|||||||
</li>
|
</li>
|
||||||
</IconContext.Provider>;
|
</IconContext.Provider>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default LayerListItem;
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {createRoot} from "react-dom/client";
|
import {createRoot} from "react-dom/client";
|
||||||
import * as MapLibreGl from "maplibre-gl";
|
import MapLibreGl, {type LayerSpecification, type LngLat, type Map, type MapOptions, type SourceSpecification, type StyleSpecification} from "maplibre-gl";
|
||||||
import {type LayerSpecification, type LngLat, type Map, type MapOptions, type SourceSpecification, type StyleSpecification} from "maplibre-gl";
|
|
||||||
import MaplibreInspect from "@maplibre/maplibre-gl-inspect";
|
import MaplibreInspect from "@maplibre/maplibre-gl-inspect";
|
||||||
import colors from "@maplibre/maplibre-gl-inspect/lib/colors";
|
import colors from "@maplibre/maplibre-gl-inspect/lib/colors";
|
||||||
import { FeatureLayerPopup as MapMaplibreGlLayerPopup } from "./MapMaplibreGlLayerPopup";
|
import MapMaplibreGlLayerPopup from "./MapMaplibreGlLayerPopup";
|
||||||
import { FeaturePropertyPopup as MapMaplibreGlFeaturePropertyPopup, type InspectFeature } from "./MapMaplibreGlFeaturePropertyPopup";
|
import MapMaplibreGlFeaturePropertyPopup, { type InspectFeature } from "./MapMaplibreGlFeaturePropertyPopup";
|
||||||
import Color from "color";
|
import Color from "color";
|
||||||
import { ZoomControl } from "../libs/zoomcontrol";
|
import ZoomControl from "../libs/zoomcontrol";
|
||||||
import { type HighlightedLayer, colorHighlightedLayer } from "../libs/highlight";
|
import { type HighlightedLayer, colorHighlightedLayer } from "../libs/highlight";
|
||||||
import "maplibre-gl/dist/maplibre-gl.css";
|
import "maplibre-gl/dist/maplibre-gl.css";
|
||||||
import "../maplibregl.css";
|
import "../maplibregl.css";
|
||||||
import "../libs/maplibre-init";
|
import "../libs/maplibre-rtl";
|
||||||
import MaplibreGeocoder, { type MaplibreGeocoderApi, type MaplibreGeocoderApiConfig } from "@maplibre/maplibre-gl-geocoder";
|
import MaplibreGeocoder, { type MaplibreGeocoderApi, type MaplibreGeocoderApiConfig } from "@maplibre/maplibre-gl-geocoder";
|
||||||
import "@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css";
|
import "@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css";
|
||||||
import { withTranslation, type WithTranslation } from "react-i18next";
|
import { withTranslation, type WithTranslation } from "react-i18next";
|
||||||
@@ -239,7 +238,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
|
|||||||
});
|
});
|
||||||
|
|
||||||
map.on("data", e => {
|
map.on("data", e => {
|
||||||
if(e.dataType !== "source" || !e.tile) return;
|
if(e.dataType !== "tile") return;
|
||||||
this.props.onDataChange!({
|
this.props.onDataChange!({
|
||||||
map: this.state.map
|
map: this.state.map
|
||||||
});
|
});
|
||||||
@@ -323,4 +322,5 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MapMaplibreGl = withTranslation()(MapMaplibreGlInternal);
|
const MapMaplibreGl = withTranslation()(MapMaplibreGlInternal);
|
||||||
|
export default MapMaplibreGl;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ type FeaturePropertyPopupProps = {
|
|||||||
features: InspectFeature[]
|
features: InspectFeature[]
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FeaturePropertyPopup extends React.Component<FeaturePropertyPopupProps> {
|
class FeaturePropertyPopup extends React.Component<FeaturePropertyPopupProps> {
|
||||||
render() {
|
render() {
|
||||||
const features = removeDuplicatedFeatures(this.props.features);
|
const features = removeDuplicatedFeatures(this.props.features);
|
||||||
return <div className="maputnik-feature-property-popup" dir="ltr" data-wd-key="feature-property-popup">
|
return <div className="maputnik-feature-property-popup" dir="ltr" data-wd-key="feature-property-popup">
|
||||||
@@ -75,3 +75,6 @@ export class FeaturePropertyPopup extends React.Component<FeaturePropertyPopupPr
|
|||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default FeaturePropertyPopup;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { IconLayer } from "./IconLayer";
|
import IconLayer from "./IconLayer";
|
||||||
import type {InspectFeature} from "./MapMaplibreGlFeaturePropertyPopup";
|
import type {InspectFeature} from "./MapMaplibreGlFeaturePropertyPopup";
|
||||||
|
|
||||||
function groupFeaturesBySourceLayer(features: InspectFeature[]) {
|
function groupFeaturesBySourceLayer(features: InspectFeature[]) {
|
||||||
@@ -32,7 +32,7 @@ type FeatureLayerPopupProps = {
|
|||||||
zoom?: number
|
zoom?: number
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FeatureLayerPopup extends React.Component<FeatureLayerPopupProps> {
|
class FeatureLayerPopup extends React.Component<FeatureLayerPopupProps> {
|
||||||
_getFeatureColor(feature: InspectFeature, _zoom?: number) {
|
_getFeatureColor(feature: InspectFeature, _zoom?: number) {
|
||||||
// Guard because openlayers won't have this
|
// Guard because openlayers won't have this
|
||||||
if (!feature.layer.paint) {
|
if (!feature.layer.paint) {
|
||||||
@@ -109,3 +109,6 @@ export class FeatureLayerPopup extends React.Component<FeatureLayerPopupProps> {
|
|||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default FeatureLayerPopup;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import {throttle} from "lodash";
|
import {throttle} from "lodash";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { FeatureLayerPopup as MapMaplibreGlLayerPopup } from "./MapMaplibreGlLayerPopup";
|
import MapMaplibreGlLayerPopup from "./MapMaplibreGlLayerPopup";
|
||||||
|
|
||||||
import "ol/ol.css";
|
import "ol/ol.css";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@@ -204,4 +204,5 @@ class MapOpenLayersInternal extends React.Component<MapOpenLayersInternalProps,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MapOpenLayers = withTranslation()(MapOpenLayersInternal);
|
const MapOpenLayers = withTranslation()(MapOpenLayersInternal);
|
||||||
|
export default MapOpenLayers;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { FieldFunction } from "./FieldFunction";
|
import FieldFunction from "./FieldFunction";
|
||||||
import type {LayerSpecification} from "maplibre-gl";
|
import type {LayerSpecification} from "maplibre-gl";
|
||||||
import { type MappedLayerErrors } from "../libs/definitions";
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ type PropertyGroupProps = {
|
|||||||
errors?: MappedLayerErrors
|
errors?: MappedLayerErrors
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PropertyGroup extends React.Component<PropertyGroupProps> {
|
export default class PropertyGroup extends React.Component<PropertyGroupProps> {
|
||||||
onPropertyChange = (property: string, newValue: any) => {
|
onPropertyChange = (property: string, newValue: any) => {
|
||||||
const group = getGroupName(this.props.spec, this.props.layer.type, property);
|
const group = getGroupName(this.props.spec, this.props.layer.type, property);
|
||||||
this.props.onChange(group ,property, newValue);
|
this.props.onChange(group ,property, newValue);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ type ScrollContainerProps = {
|
|||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ScrollContainer extends React.Component<ScrollContainerProps> {
|
export default class ScrollContainer extends React.Component<ScrollContainerProps> {
|
||||||
render() {
|
render() {
|
||||||
return <div className="maputnik-scroll-container">
|
return <div className="maputnik-scroll-container">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import {otherFilterOps} from "../libs/filterops";
|
import {otherFilterOps} from "../libs/filterops";
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
import { InputAutocomplete } from "./InputAutocomplete";
|
import InputAutocomplete from "./InputAutocomplete";
|
||||||
import { InputSelect } from "./InputSelect";
|
import InputSelect from "./InputSelect";
|
||||||
|
|
||||||
function tryParseInt(v: string | number) {
|
function tryParseInt(v: string | number) {
|
||||||
if (v === "") return v;
|
if (v === "") return v;
|
||||||
@@ -40,7 +40,7 @@ type SingleFilterEditorProps = {
|
|||||||
properties?: {[key: string]: string}
|
properties?: {[key: string]: string}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SingleFilterEditor extends React.Component<SingleFilterEditorProps> {
|
export default class SingleFilterEditor extends React.Component<SingleFilterEditorProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
properties: {},
|
properties: {},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,4 +18,5 @@ class SmallErrorInternal extends React.Component<SmallErrorInternalProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SmallError = withTranslation()(SmallErrorInternal);
|
const SmallError = withTranslation()(SmallErrorInternal);
|
||||||
|
export default SmallError;
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import {PiListPlusBold} from "react-icons/pi";
|
|||||||
import {TbMathFunction} from "react-icons/tb";
|
import {TbMathFunction} from "react-icons/tb";
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
|
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import { InputSpec } from "./InputSpec";
|
import InputSpec from "./InputSpec";
|
||||||
import { InputNumber } from "./InputNumber";
|
import InputNumber from "./InputNumber";
|
||||||
import { InputString } from "./InputString";
|
import InputString from "./InputString";
|
||||||
import { InputSelect } from "./InputSelect";
|
import InputSelect from "./InputSelect";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { generateUniqueId as docUid } from "../libs/document-uid";
|
import docUid from "../libs/document-uid";
|
||||||
import { sortNumerically } from "../libs/sort-numerically";
|
import sortNumerically from "../libs/sort-numerically";
|
||||||
import {findDefaultFromSpec} from "../libs/spec-helper";
|
import {findDefaultFromSpec} from "../libs/spec-helper";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { labelFromFieldName } from "../libs/label-from-field-name";
|
import labelFromFieldName from "../libs/label-from-field-name";
|
||||||
import { DeleteStopButton } from "./DeleteStopButton";
|
import DeleteStopButton from "./_DeleteStopButton";
|
||||||
import { type MappedLayerErrors } from "../libs/definitions";
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
@@ -385,4 +385,5 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DataProperty = withTranslation()(DataPropertyInternal);
|
const DataProperty = withTranslation()(DataPropertyInternal);
|
||||||
|
export default DataProperty;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import {MdDelete} from "react-icons/md";
|
import {MdDelete} from "react-icons/md";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
@@ -23,4 +23,5 @@ class DeleteStopButtonInternal extends React.Component<DeleteStopButtonInternalP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DeleteStopButton = withTranslation()(DeleteStopButtonInternal);
|
const DeleteStopButton = withTranslation()(DeleteStopButtonInternal);
|
||||||
|
export default DeleteStopButton;
|
||||||
@@ -2,10 +2,10 @@ import React from "react";
|
|||||||
import {MdDelete, MdUndo} from "react-icons/md";
|
import {MdDelete, MdUndo} from "react-icons/md";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import { labelFromFieldName } from "../libs/label-from-field-name";
|
import labelFromFieldName from "../libs/label-from-field-name";
|
||||||
import { FieldJson } from "./FieldJson";
|
import FieldJson from "./FieldJson";
|
||||||
import type { StylePropertySpecification } from "maplibre-gl";
|
import type { StylePropertySpecification } from "maplibre-gl";
|
||||||
import { type MappedLayerErrors } from "../libs/definitions";
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
@@ -92,4 +92,5 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExpressionProperty = withTranslation()(ExpressionPropertyInternal);
|
const ExpressionProperty = withTranslation()(ExpressionPropertyInternal);
|
||||||
|
export default ExpressionProperty;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import {MdFunctions, MdInsertChart} from "react-icons/md";
|
import {MdFunctions, MdInsertChart} from "react-icons/md";
|
||||||
import { TbMathFunction } from "react-icons/tb";
|
import { TbMathFunction } from "react-icons/tb";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
@@ -67,4 +67,5 @@ class FunctionInputButtonsInternal extends React.Component<FunctionInputButtonsI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FunctionInputButtons = withTranslation()(FunctionInputButtonsInternal);
|
const FunctionInputButtons = withTranslation()(FunctionInputButtonsInternal);
|
||||||
|
export default FunctionInputButtons;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { FieldSpec, type FieldSpecProps } from "./FieldSpec";
|
import FieldSpec, {type FieldSpecProps} from "./FieldSpec";
|
||||||
import { FunctionInputButtons as FunctionButtons } from "./FunctionButtons";
|
import FunctionButtons from "./_FunctionButtons";
|
||||||
|
|
||||||
import { labelFromFieldName } from "../libs/label-from-field-name";
|
import labelFromFieldName from "../libs/label-from-field-name";
|
||||||
|
|
||||||
|
|
||||||
type SpecPropertyProps = FieldSpecProps & {
|
type SpecPropertyProps = FieldSpecProps & {
|
||||||
@@ -19,7 +19,7 @@ type SpecPropertyProps = FieldSpecProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export class SpecProperty extends React.Component<SpecPropertyProps> {
|
export default class SpecProperty extends React.Component<SpecPropertyProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
errors: {},
|
errors: {},
|
||||||
};
|
};
|
||||||
@@ -4,17 +4,17 @@ import { TbMathFunction } from "react-icons/tb";
|
|||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { InputButton } from "./InputButton";
|
import InputButton from "./InputButton";
|
||||||
import { InputSpec } from "./InputSpec";
|
import InputSpec from "./InputSpec";
|
||||||
import { InputNumber } from "./InputNumber";
|
import InputNumber from "./InputNumber";
|
||||||
import { InputSelect } from "./InputSelect";
|
import InputSelect from "./InputSelect";
|
||||||
import { Block } from "./Block";
|
import Block from "./Block";
|
||||||
|
|
||||||
import { DeleteStopButton } from "./DeleteStopButton";
|
import DeleteStopButton from "./_DeleteStopButton";
|
||||||
import { labelFromFieldName } from "../libs/label-from-field-name";
|
import labelFromFieldName from "../libs/label-from-field-name";
|
||||||
|
|
||||||
import { generateUniqueId as docUid } from "../libs/document-uid";
|
import docUid from "../libs/document-uid";
|
||||||
import { sortNumerically } from "../libs/sort-numerically";
|
import sortNumerically from "../libs/sort-numerically";
|
||||||
import { type MappedLayerErrors } from "../libs/definitions";
|
import { type MappedLayerErrors } from "../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
@@ -267,4 +267,5 @@ class ZoomPropertyInternal extends React.Component<ZoomPropertyInternalProps, Zo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ZoomProperty = withTranslation()(ZoomPropertyInternal);
|
const ZoomProperty = withTranslation()(ZoomPropertyInternal);
|
||||||
|
export default ZoomProperty;
|
||||||
@@ -67,4 +67,5 @@ class ModalInternal extends React.Component<ModalInternalProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Modal = withTranslation()(ModalInternal);
|
const Modal = withTranslation()(ModalInternal);
|
||||||
|
export default Modal;
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import React from "react";
|
|||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
import type {LayerSpecification, SourceSpecification} from "maplibre-gl";
|
import type {LayerSpecification, SourceSpecification} from "maplibre-gl";
|
||||||
|
|
||||||
import { InputButton } from "../InputButton";
|
import InputButton from "../InputButton";
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
import { FieldType } from "../FieldType";
|
import FieldType from "../FieldType";
|
||||||
import { FieldId } from "../FieldId";
|
import FieldId from "../FieldId";
|
||||||
import { FieldSource } from "../FieldSource";
|
import FieldSource from "../FieldSource";
|
||||||
import { FieldSourceLayer } from "../FieldSourceLayer";
|
import FieldSourceLayer from "../FieldSourceLayer";
|
||||||
import { NON_SOURCE_LAYERS } from "../../libs/non-source-layers";
|
import { NON_SOURCE_LAYERS } from "../../libs/non-source-layers";
|
||||||
|
|
||||||
type ModalAddInternalProps = {
|
type ModalAddInternalProps = {
|
||||||
@@ -192,4 +192,5 @@ class ModalAddInternal extends React.Component<ModalAddInternalProps, ModalAddSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalAdd = withTranslation()(ModalAddInternal);
|
const ModalAdd = withTranslation()(ModalAddInternal);
|
||||||
|
export default ModalAdd;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
|
|
||||||
|
|
||||||
type ModalDebugInternalProps = {
|
type ModalDebugInternalProps = {
|
||||||
@@ -79,4 +79,5 @@ class ModalDebugInternal extends React.Component<ModalDebugInternalProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalDebug = withTranslation()(ModalDebugInternal);
|
const ModalDebug = withTranslation()(ModalDebugInternal);
|
||||||
|
export default ModalDebug;
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import {format} from "@maplibre/maplibre-gl-style-spec";
|
|||||||
import {MdMap, MdSave} from "react-icons/md";
|
import {MdMap, MdSave} from "react-icons/md";
|
||||||
import {type WithTranslation, withTranslation} from "react-i18next";
|
import {type WithTranslation, withTranslation} from "react-i18next";
|
||||||
|
|
||||||
import { FieldString } from "../FieldString";
|
import FieldString from "../FieldString";
|
||||||
import { InputButton } from "../InputButton";
|
import InputButton from "../InputButton";
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
import {replaceAccessTokens, stripAccessTokens} from "../../libs/style";
|
import {replaceAccessTokens, stripAccessTokens} from "../../libs/style";
|
||||||
import { spec as fieldSpecAdditional } from "../../libs/field-spec-additional";
|
import fieldSpecAdditional from "../../libs/field-spec-additional";
|
||||||
import type {OnStyleChangedCallback, StyleSpecificationWithId} from "../../libs/definitions";
|
import type {OnStyleChangedCallback, StyleSpecificationWithId} from "../../libs/definitions";
|
||||||
|
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ class ModalExportInternal extends React.Component<ModalExportInternalProps> {
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>${htmlTitle}</title>
|
<title>${htmlTitle}</title>
|
||||||
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
|
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
|
||||||
|
<script src="https://unpkg.com/maplibre-gl@${MAPLIBRE_GL_VERSION}/dist/maplibre-gl.js"></script>
|
||||||
<link href="https://unpkg.com/maplibre-gl@${MAPLIBRE_GL_VERSION}/dist/maplibre-gl.css" rel="stylesheet" />
|
<link href="https://unpkg.com/maplibre-gl@${MAPLIBRE_GL_VERSION}/dist/maplibre-gl.css" rel="stylesheet" />
|
||||||
<style>
|
<style>
|
||||||
body { margin: 0; padding: 0; }
|
body { margin: 0; padding: 0; }
|
||||||
@@ -67,9 +68,8 @@ class ModalExportInternal extends React.Component<ModalExportInternalProps> {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<script type="module">
|
<script>
|
||||||
import {Map} from 'https://unpkg.com/maplibre-gl@${MAPLIBRE_GL_VERSION}/dist/maplibre-gl.mjs';
|
const map = new maplibregl.Map({
|
||||||
const map = new Map({
|
|
||||||
container: 'map',
|
container: 'map',
|
||||||
style: ${tokenStyle},
|
style: ${tokenStyle},
|
||||||
});
|
});
|
||||||
@@ -217,4 +217,5 @@ class ModalExportInternal extends React.Component<ModalExportInternalProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalExport = withTranslation()(ModalExportInternal);
|
const ModalExport = withTranslation()(ModalExportInternal);
|
||||||
|
export default ModalExport;
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { withTranslation, type WithTranslation } from "react-i18next";
|
|||||||
import { MdDelete } from "react-icons/md";
|
import { MdDelete } from "react-icons/md";
|
||||||
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
||||||
|
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
import { FieldString } from "../FieldString";
|
import FieldString from "../FieldString";
|
||||||
import { InputButton } from "../InputButton";
|
import InputButton from "../InputButton";
|
||||||
import { PiListPlusBold } from "react-icons/pi";
|
import { PiListPlusBold } from "react-icons/pi";
|
||||||
import { type StyleSpecificationWithId } from "../../libs/definitions";
|
import { type StyleSpecificationWithId } from "../../libs/definitions";
|
||||||
import { type SchemaSpecification } from "maplibre-gl";
|
import { type SchemaSpecification } from "maplibre-gl";
|
||||||
import { Doc } from "../Doc";
|
import Doc from "../Doc";
|
||||||
|
|
||||||
type ModalGlobalStateInternalProps = {
|
type ModalGlobalStateInternalProps = {
|
||||||
mapStyle: StyleSpecificationWithId;
|
mapStyle: StyleSpecificationWithId;
|
||||||
@@ -151,4 +151,5 @@ const ModalGlobalStateInternal: React.FC<ModalGlobalStateInternalProps> = (props
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ModalGlobalState = withTranslation()(ModalGlobalStateInternal);
|
const ModalGlobalState = withTranslation()(ModalGlobalStateInternal);
|
||||||
|
export default ModalGlobalState;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { InputButton } from "../InputButton";
|
import InputButton from "../InputButton";
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
|
|
||||||
|
|
||||||
type ModalLoadingInternalProps = {
|
type ModalLoadingInternalProps = {
|
||||||
@@ -35,4 +35,5 @@ class ModalLoadingInternal extends React.Component<ModalLoadingInternalProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalLoading = withTranslation()(ModalLoadingInternal);
|
const ModalLoading = withTranslation()(ModalLoadingInternal);
|
||||||
|
export default ModalLoading;
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { MdFileUpload } from "react-icons/md";
|
|||||||
import { MdAddCircleOutline } from "react-icons/md";
|
import { MdAddCircleOutline } from "react-icons/md";
|
||||||
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { ModalLoading } from "./ModalLoading";
|
import ModalLoading from "./ModalLoading";
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
import { InputButton } from "../InputButton";
|
import InputButton from "../InputButton";
|
||||||
import { InputUrl } from "../InputUrl";
|
import InputUrl from "../InputUrl";
|
||||||
|
|
||||||
import { ensureStyleValidity } from "../../libs/style";
|
import { ensureStyleValidity } from "../../libs/style";
|
||||||
import publicStyles from "../../config/styles.json";
|
import publicStyles from "../../config/styles.json";
|
||||||
@@ -364,4 +364,5 @@ class ModalOpenInternal extends React.Component<ModalOpenInternalProps, ModalOpe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalOpen = withTranslation()(ModalOpenInternal);
|
const ModalOpen = withTranslation()(ModalOpenInternal);
|
||||||
|
export default ModalOpen;
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
|||||||
import type {LightSpecification, ProjectionSpecification, StyleSpecification, TerrainSpecification, TransitionSpecification} from "maplibre-gl";
|
import type {LightSpecification, ProjectionSpecification, StyleSpecification, TerrainSpecification, TransitionSpecification} from "maplibre-gl";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { FieldArray } from "../FieldArray";
|
import FieldArray from "../FieldArray";
|
||||||
import { FieldNumber } from "../FieldNumber";
|
import FieldNumber from "../FieldNumber";
|
||||||
import { FieldString } from "../FieldString";
|
import FieldString from "../FieldString";
|
||||||
import { FieldUrl } from "../FieldUrl";
|
import FieldUrl from "../FieldUrl";
|
||||||
import { FieldSelect } from "../FieldSelect";
|
import FieldSelect from "../FieldSelect";
|
||||||
import { FieldEnum } from "../FieldEnum";
|
import FieldEnum from "../FieldEnum";
|
||||||
import { FieldColor } from "../FieldColor";
|
import FieldColor from "../FieldColor";
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
import { FieldJson } from "../FieldJson";
|
import FieldJson from "../FieldJson";
|
||||||
import { Block } from "../Block";
|
import Block from "../Block";
|
||||||
import { spec as fieldSpecAdditional } from "../../libs/field-spec-additional";
|
import fieldSpecAdditional from "../../libs/field-spec-additional";
|
||||||
import type {OnStyleChangedCallback, StyleSpecificationWithId} from "../../libs/definitions";
|
import type {OnStyleChangedCallback, StyleSpecificationWithId} from "../../libs/definitions";
|
||||||
|
|
||||||
type ModalSettingsInternalProps = {
|
type ModalSettingsInternalProps = {
|
||||||
@@ -332,4 +332,5 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalSettings = withTranslation()(ModalSettingsInternal);
|
const ModalSettings = withTranslation()(ModalSettingsInternal);
|
||||||
|
export default ModalSettings;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
import { Trans, type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
|
|
||||||
|
|
||||||
type ModalShortcutsInternalProps = {
|
type ModalShortcutsInternalProps = {
|
||||||
@@ -134,4 +134,5 @@ class ModalShortcutsInternal extends React.Component<ModalShortcutsInternalProps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalShortcuts = withTranslation()(ModalShortcutsInternal);
|
const ModalShortcuts = withTranslation()(ModalShortcutsInternal);
|
||||||
|
export default ModalShortcuts;
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
|
|||||||
import type {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, VectorSourceSpecification} from "maplibre-gl";
|
import type {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, VectorSourceSpecification} from "maplibre-gl";
|
||||||
import { type WithTranslation, withTranslation } from "react-i18next";
|
import { type WithTranslation, withTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { Modal } from "./Modal";
|
import Modal from "./Modal";
|
||||||
import { InputButton } from "../InputButton";
|
import InputButton from "../InputButton";
|
||||||
import { FieldString } from "../FieldString";
|
import FieldString from "../FieldString";
|
||||||
import { FieldSelect } from "../FieldSelect";
|
import FieldSelect from "../FieldSelect";
|
||||||
import { ModalSourcesTypeEditor, type EditorMode } from "./ModalSourcesTypeEditor";
|
import ModalSourcesTypeEditor, { type EditorMode } from "./ModalSourcesTypeEditor";
|
||||||
|
|
||||||
import { generateId } from "../../libs/style";
|
import { generateId } from "../../libs/style";
|
||||||
import { deleteSource, addSource, changeSource } from "../../libs/source";
|
import { deleteSource, addSource, changeSource } from "../../libs/source";
|
||||||
@@ -343,4 +343,5 @@ class ModalSourcesInternal extends React.Component<ModalSourcesInternalProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModalSources = withTranslation()(ModalSourcesInternal);
|
const ModalSources = withTranslation()(ModalSourcesInternal);
|
||||||
|
export default ModalSources;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user