mirror of
https://github.com/maputnik/editor.git
synced 2026-08-27 15:37:27 +00:00
Compare commits
13 Commits
6f4c34b29a
...
v3.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fc334ad85 | |||
| 42a040e91a | |||
| f5757dd8a2 | |||
| 3b79303f83 | |||
| 55a487d0c8 | |||
| 03f3c5c032 | |||
| 02efeec96d | |||
| 4896c40554 | |||
| 40f1837835 | |||
| 3f94ab2f3b | |||
| 0cd4ab6e0a | |||
| fce182050d | |||
| abe6230932 |
+23
-17
@@ -7,17 +7,7 @@ on:
|
|||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-docker:
|
|
||||||
name: build docker
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
- run: docker build -t test-docker-image-build .
|
|
||||||
|
|
||||||
# build the editor
|
|
||||||
build-node:
|
build-node:
|
||||||
name: "build on ${{ matrix.os }}"
|
name: "build on ${{ matrix.os }}"
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -89,13 +79,9 @@ jobs:
|
|||||||
path: ./desktop/bin/windows/
|
path: ./desktop/bin/windows/
|
||||||
|
|
||||||
e2e-tests:
|
e2e-tests:
|
||||||
name: "E2E tests using ${{ matrix.browser }}"
|
name: "E2E tests using chrome"
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
browser: [ chrome ]
|
|
||||||
|
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
@@ -105,7 +91,27 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
build: npm run build
|
build: npm run build
|
||||||
start: npm run start
|
start: npm run start
|
||||||
browser: ${{ matrix.browser }}
|
browser: chrome
|
||||||
|
- name: Upload coverage reports to Codecov
|
||||||
|
uses: codecov/codecov-action@v5
|
||||||
|
with:
|
||||||
|
files: ${{ github.workspace }}/.nyc_output/out.json
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
e2e-tests-docker:
|
||||||
|
name: "E2E tests using chrome and docker"
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- run: npm ci
|
||||||
|
- name: Cypress run
|
||||||
|
uses: cypress-io/github-action@v6
|
||||||
|
with:
|
||||||
|
build: docker build -t maputnik .
|
||||||
|
start: docker run --rm --network host maputnik --port=8888
|
||||||
|
browser: chrome
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,14 @@
|
|||||||
## main
|
## main
|
||||||
|
|
||||||
|
### ✨ Features and improvements
|
||||||
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
|
### 🐞 Bug fixes
|
||||||
|
|
||||||
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
### ✨ Features and improvements
|
### ✨ Features and improvements
|
||||||
- Fix radio/delete filter buttons styling regression
|
- Fix radio/delete filter buttons styling regression
|
||||||
- Add german translation
|
- Add german translation
|
||||||
@@ -17,7 +26,9 @@
|
|||||||
- Refactor Field components to use arrow function syntax
|
- Refactor Field components to use arrow function syntax
|
||||||
- Replace react-autocomplete with Downshift in the autocomplete component
|
- Replace react-autocomplete with Downshift in the autocomplete component
|
||||||
- Add LocationIQ as supported map provider with access token field and gallery style
|
- Add LocationIQ as supported map provider with access token field and gallery style
|
||||||
- _...Add new stuff here..._
|
- Use maputnik go binary for the docker image to allow file watching
|
||||||
|
- Revmove support for `debug` and `localport` url parameters
|
||||||
|
- Replace react-sortable-hoc with dnd-kit to avoid react console warnings and also use a maintained library
|
||||||
|
|
||||||
### 🐞 Bug fixes
|
### 🐞 Bug fixes
|
||||||
|
|
||||||
@@ -25,7 +36,6 @@
|
|||||||
- Show an error when adding a layer with a duplicate ID
|
- Show an error when adding a layer with a duplicate ID
|
||||||
- Replace deprecated `ReactDOM.render` usage with `createRoot` and drop the
|
- Replace deprecated `ReactDOM.render` usage with `createRoot` and drop the
|
||||||
`DOMNodeRemoved` cleanup hack
|
`DOMNodeRemoved` cleanup hack
|
||||||
- _...Add new stuff here..._
|
|
||||||
|
|
||||||
## 2.1.1
|
## 2.1.1
|
||||||
|
|
||||||
|
|||||||
+8
-10
@@ -1,16 +1,14 @@
|
|||||||
FROM node:22 as builder
|
FROM golang:1.23-alpine AS builder
|
||||||
WORKDIR /maputnik
|
WORKDIR /maputnik
|
||||||
|
|
||||||
# Only copy package.json to prevent npm install from running on every build
|
RUN apk add --no-cache nodejs npm make git gcc g++ libc-dev
|
||||||
COPY package.json package-lock.json .npmrc ./
|
|
||||||
RUN npm ci
|
|
||||||
|
|
||||||
# Build maputnik
|
# Build maputnik
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npx vite build
|
RUN npm ci
|
||||||
|
RUN CGO_ENABLED=1 GOOS=linux npm run build-linux
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
FROM alpine:latest
|
||||||
# Create a clean nginx-alpine slim image with just the build results
|
WORKDIR /app
|
||||||
FROM nginx:alpine-slim
|
COPY --from=builder /maputnik/desktop/bin/linux ./
|
||||||
|
ENTRYPOINT ["/app/maputnik"]
|
||||||
COPY --from=builder /maputnik/dist /usr/share/nginx/html/
|
|
||||||
|
|||||||
@@ -18,9 +18,15 @@ targeted at developers and map designers.
|
|||||||
- In a Docker, run this command and browse to http://localhost:8888, Ctrl+C to stop the server.
|
- In a Docker, run this command and browse to http://localhost:8888, Ctrl+C to stop the server.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm -p 8888:80 ghcr.io/maplibre/maputnik:main
|
docker run -it --rm -p 8888:8000 ghcr.io/maplibre/maputnik:main
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To see the CLI options (for example file watching or style serving) run:
|
||||||
|
```bash
|
||||||
|
docker run -it --rm -p 8888:8000 ghcr.io/maplibre/maputnik:main --help
|
||||||
|
```
|
||||||
|
You might need to mount a volume (`-v`) to be able to use these options.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The documentation can be found in the [Wiki](https://github.com/maplibre/maputnik/wiki). You are welcome to collaborate!
|
The documentation can be found in the [Wiki](https://github.com/maplibre/maputnik/wiki). You are welcome to collaborate!
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ describe("history", () => {
|
|||||||
it("undo/redo", () => {
|
it("undo/redo", () => {
|
||||||
when.setStyle("geojson");
|
when.setStyle("geojson");
|
||||||
when.modal.open();
|
when.modal.open();
|
||||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ layers: [] });
|
|
||||||
|
|
||||||
when.modal.fillLayers({
|
when.modal.fillLayers({
|
||||||
id: "step 1",
|
id: "step 1",
|
||||||
|
|||||||
@@ -378,8 +378,57 @@ describe("layers", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("groups", () => {
|
it("groups", () => {
|
||||||
// TODO
|
when.modal.open();
|
||||||
// Click each of the layer groups.
|
const id1 = when.modal.fillLayers({
|
||||||
|
id: "aa",
|
||||||
|
type: "line",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
when.modal.open();
|
||||||
|
const id2 = when.modal.fillLayers({
|
||||||
|
id: "aa-2",
|
||||||
|
type: "line",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
when.modal.open();
|
||||||
|
const id3 = when.modal.fillLayers({
|
||||||
|
id: "b",
|
||||||
|
type: "line",
|
||||||
|
layer: "example",
|
||||||
|
});
|
||||||
|
|
||||||
|
then(get.elementByTestId("layer-list-item:" + id1)).shouldBeVisible();
|
||||||
|
then(get.elementByTestId("layer-list-item:" + id2)).shouldNotBeVisible();
|
||||||
|
then(get.elementByTestId("layer-list-item:" + id3)).shouldBeVisible();
|
||||||
|
when.click("layer-list-group:aa-0");
|
||||||
|
then(get.elementByTestId("layer-list-item:" + id1)).shouldBeVisible();
|
||||||
|
then(get.elementByTestId("layer-list-item:" + id2)).shouldBeVisible();
|
||||||
|
then(get.elementByTestId("layer-list-item:" + id3)).shouldBeVisible();
|
||||||
|
when.click("layer-list-item:" + id2);
|
||||||
|
when.click("skip-target-layer-editor");
|
||||||
|
when.click("menu-move-layer-down");
|
||||||
|
then(get.elementByTestId("layer-list-group:aa-0")).shouldNotExist();
|
||||||
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
id: "aa",
|
||||||
|
type: "line",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "b",
|
||||||
|
type: "line",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "aa-2",
|
||||||
|
type: "line",
|
||||||
|
source: "example",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -495,9 +544,7 @@ describe("layers", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe("layereditor jsonlint should error", ()=>{
|
describe("layereditor jsonlint should error", ()=>{
|
||||||
|
|
||||||
it("add", () => {
|
it("add", () => {
|
||||||
const id = when.modal.fillLayers({
|
const id = when.modal.fillLayers({
|
||||||
type: "circle",
|
type: "circle",
|
||||||
@@ -523,4 +570,42 @@ describe("layers", () => {
|
|||||||
error.should('exist');
|
error.should('exist');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("drag and drop", () => {
|
||||||
|
it("move layer should update local storage", () => {
|
||||||
|
when.modal.open();
|
||||||
|
const firstId = when.modal.fillLayers({
|
||||||
|
id: "a",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
when.modal.open();
|
||||||
|
const secondId = when.modal.fillLayers({
|
||||||
|
id: "b",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
when.modal.open();
|
||||||
|
const thirdId = when.modal.fillLayers({
|
||||||
|
id: "c",
|
||||||
|
type: "background",
|
||||||
|
});
|
||||||
|
when.dragAndDrop(get.elementByTestId("layer-list-item:" + firstId), get.elementByTestId("layer-list-item:" + thirdId));
|
||||||
|
|
||||||
|
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||||
|
layers: [
|
||||||
|
{
|
||||||
|
id: secondId,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: thirdId,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: firstId,
|
||||||
|
type: "background",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ export class MaputnikDriver {
|
|||||||
public when = {
|
public when = {
|
||||||
...this.helper.when,
|
...this.helper.when,
|
||||||
modal: this.modalDriver.when,
|
modal: this.modalDriver.when,
|
||||||
within: (selector: string, fn: () => void) => {
|
doWithin: (selector: string, fn: () => void) => {
|
||||||
this.helper.when.within(fn, selector);
|
this.helper.when.doWithin(fn, selector);
|
||||||
},
|
},
|
||||||
tab: () => this.helper.get.element("body").tab(),
|
tab: () => this.helper.get.element("body").tab(),
|
||||||
waitForExampleFileResponse: () => {
|
waitForExampleFileResponse: () => {
|
||||||
@@ -110,25 +110,25 @@ export class MaputnikDriver {
|
|||||||
styleProperties: "geojson" | "raster" | "both" | "layer" | "",
|
styleProperties: "geojson" | "raster" | "both" | "layer" | "",
|
||||||
zoom?: number
|
zoom?: number
|
||||||
) => {
|
) => {
|
||||||
let url = "?debug";
|
const url = new URL(baseUrl);
|
||||||
switch (styleProperties) {
|
switch (styleProperties) {
|
||||||
case "geojson":
|
case "geojson":
|
||||||
url += `&style=${baseUrl}geojson-style.json`;
|
url.searchParams.set("style", baseUrl + "geojson-style.json");
|
||||||
break;
|
break;
|
||||||
case "raster":
|
case "raster":
|
||||||
url += `&style=${baseUrl}raster-style.json`;
|
url.searchParams.set("style", baseUrl + "raster-style.json");
|
||||||
break;
|
break;
|
||||||
case "both":
|
case "both":
|
||||||
url += `&style=${baseUrl}geojson-raster-style.json`;
|
url.searchParams.set("style", baseUrl + "geojson-raster-style.json");
|
||||||
break;
|
break;
|
||||||
case "layer":
|
case "layer":
|
||||||
url += `&style=${baseUrl}/example-layer-style.json`;
|
url.searchParams.set("style", baseUrl + "example-layer-style.json");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (zoom) {
|
if (zoom) {
|
||||||
url += `#${zoom}/41.3805/2.1635`;
|
url.hash = `${zoom}/41.3805/2.1635`;
|
||||||
}
|
}
|
||||||
this.helper.when.visit(baseUrl + url);
|
this.helper.when.visit(url.toString());
|
||||||
if (styleProperties) {
|
if (styleProperties) {
|
||||||
this.helper.when.acceptConfirm();
|
this.helper.when.acceptConfirm();
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ export class MaputnikDriver {
|
|||||||
},
|
},
|
||||||
|
|
||||||
selectWithin: (selector: string, value: string) => {
|
selectWithin: (selector: string, value: string) => {
|
||||||
this.when.within(selector, () => {
|
this.when.doWithin(selector, () => {
|
||||||
this.helper.get.element("select").select(value);
|
this.helper.get.element("select").select(value);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export default class ModalDriver {
|
|||||||
this.helper.when.type("add-layer.layer-id.input", id);
|
this.helper.when.type("add-layer.layer-id.input", id);
|
||||||
|
|
||||||
if (layer) {
|
if (layer) {
|
||||||
this.helper.when.within(() => {
|
this.helper.when.doWithin(() => {
|
||||||
this.helper.get.element("input").type(layer!);
|
this.helper.get.element("input").clear().type(layer!);
|
||||||
}, "add-layer.layer-source-block");
|
}, "add-layer.layer-source-block");
|
||||||
}
|
}
|
||||||
this.helper.when.click("add-layer");
|
this.helper.when.click("add-layer");
|
||||||
|
|||||||
+5
-2
@@ -2,8 +2,7 @@ SOURCEDIR=.
|
|||||||
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
|
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
|
||||||
BINARY=maputnik
|
BINARY=maputnik
|
||||||
VERSION := $(shell node -p "require('../package.json').version")
|
VERSION := $(shell node -p "require('../package.json').version")
|
||||||
GOPATH := $(if $(GOPATH),$(GOPATH),$(HOME)/go)
|
GOBIN := $(or $(shell if [ -d /go/bin ]; then echo "/go/bin"; fi),$(HOME)/go/bin)
|
||||||
GOBIN := $(if $(GOBIN),$(GOBIN),$(HOME)/go/bin)
|
|
||||||
|
|
||||||
all: $(BINARY)
|
all: $(BINARY)
|
||||||
|
|
||||||
@@ -11,6 +10,10 @@ $(BINARY): $(GOBIN)/gox $(GOBIN)/go-winres $(SOURCES) version.go rice-box.go win
|
|||||||
$(GOBIN)/go-winres make --product-version=$(VERSION)
|
$(GOBIN)/go-winres make --product-version=$(VERSION)
|
||||||
$(GOBIN)/gox -osarch "windows/amd64 linux/amd64 darwin/amd64" -output "bin/{{.OS}}/${BINARY}"
|
$(GOBIN)/gox -osarch "windows/amd64 linux/amd64 darwin/amd64" -output "bin/{{.OS}}/${BINARY}"
|
||||||
|
|
||||||
|
bin/linux/$(BINARY): $(GOBIN)/gox $(GOBIN)/go-winres $(SOURCES) version.go rice-box.go winres/winres.json
|
||||||
|
$(GOBIN)/go-winres make --product-version=$(VERSION)
|
||||||
|
$(GOBIN)/gox -osarch "linux/amd64" -output "bin/{{.OS}}/${BINARY}"
|
||||||
|
|
||||||
winres/winres.json: winres/winres_template.json
|
winres/winres.json: winres/winres_template.json
|
||||||
sed 's/{{.Version}}/$(VERSION)/g' winres/winres_template.json > $@
|
sed 's/{{.Version}}/$(VERSION)/g' winres/winres_template.json > $@
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -5,8 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"github.com/GeertJohan/go.rice"
|
||||||
"github.com/GeertJohan/go.rice"
|
|
||||||
"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"
|
||||||
|
|||||||
Generated
+187
-153
@@ -1,14 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "maputnik",
|
"name": "maputnik",
|
||||||
"version": "2.1.1",
|
"version": "3.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "maputnik",
|
"name": "maputnik",
|
||||||
"version": "2.1.1",
|
"version": "3.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@dnd-kit/core": "^6.3.1",
|
||||||
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "^0.3.0",
|
"@mapbox/mapbox-gl-rtl-text": "^0.3.0",
|
||||||
"@maplibre/maplibre-gl-geocoder": "^1.9.0",
|
"@maplibre/maplibre-gl-geocoder": "^1.9.0",
|
||||||
"@maplibre/maplibre-gl-inspect": "^1.7.1",
|
"@maplibre/maplibre-gl-inspect": "^1.7.1",
|
||||||
@@ -37,7 +40,7 @@
|
|||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"maplibre-gl": "^5.6.2",
|
"maplibre-gl": "^5.7.1",
|
||||||
"maputnik-design": "github:maputnik/design#172b06c",
|
"maputnik-design": "github:maputnik/design#172b06c",
|
||||||
"ol": "^10.6.1",
|
"ol": "^10.6.1",
|
||||||
"ol-mapbox-style": "^13.1.0",
|
"ol-mapbox-style": "^13.1.0",
|
||||||
@@ -54,7 +57,6 @@
|
|||||||
"react-i18next": "^15.7.3",
|
"react-i18next": "^15.7.3",
|
||||||
"react-icon-base": "^2.1.2",
|
"react-icon-base": "^2.1.2",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-sortable-hoc": "^2.0.0",
|
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"string-hash": "^1.1.3",
|
"string-hash": "^1.1.3",
|
||||||
@@ -62,7 +64,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/code-coverage": "^3.14.6",
|
"@cypress/code-coverage": "^3.14.6",
|
||||||
"@eslint/js": "^9.34.0",
|
"@eslint/js": "^9.35.0",
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
"@rollup/plugin-replace": "^6.0.2",
|
"@rollup/plugin-replace": "^6.0.2",
|
||||||
"@shellygo/cypress-test-utils": "^6.0.1",
|
"@shellygo/cypress-test-utils": "^6.0.1",
|
||||||
@@ -94,7 +96,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cypress": "^15.1.0",
|
"cypress": "^15.1.0",
|
||||||
"cypress-plugin-tab": "^1.0.5",
|
"cypress-plugin-tab": "^1.0.5",
|
||||||
"eslint": "^9.34.0",
|
"eslint": "^9.35.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
@@ -103,14 +105,14 @@
|
|||||||
"istanbul-lib-coverage": "^3.2.2",
|
"istanbul-lib-coverage": "^3.2.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react-hot-loader": "^4.13.1",
|
"react-hot-loader": "^4.13.1",
|
||||||
"sass": "^1.92.0",
|
"sass": "^1.92.1",
|
||||||
"stylelint": "^16.23.1",
|
"stylelint": "^16.24.0",
|
||||||
"stylelint-config-recommended-scss": "^16.0.0",
|
"stylelint-config-recommended-scss": "^16.0.0",
|
||||||
"stylelint-scss": "^6.12.1",
|
"stylelint-scss": "^6.12.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.42.0",
|
"typescript-eslint": "^8.43.0",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^13.0.0",
|
||||||
"vite": "^7.1.4",
|
"vite": "^7.1.5",
|
||||||
"vite-plugin-istanbul": "^7.1.0"
|
"vite-plugin-istanbul": "^7.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -698,6 +700,59 @@
|
|||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@dnd-kit/accessibility": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dnd-kit/core": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@dnd-kit/accessibility": "^3.1.1",
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dnd-kit/sortable": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@dnd-kit/core": "^6.3.0",
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dnd-kit/utilities": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@dual-bundle/import-meta-resolve": {
|
"node_modules/@dual-bundle/import-meta-resolve": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
|
||||||
@@ -1135,9 +1190,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.7.0",
|
"version": "4.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
||||||
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
|
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1284,9 +1339,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.34.0",
|
"version": "9.35.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz",
|
||||||
"integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==",
|
"integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3277,17 +3332,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz",
|
||||||
"integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==",
|
"integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.42.0",
|
"@typescript-eslint/scope-manager": "8.43.0",
|
||||||
"@typescript-eslint/type-utils": "8.42.0",
|
"@typescript-eslint/type-utils": "8.43.0",
|
||||||
"@typescript-eslint/utils": "8.42.0",
|
"@typescript-eslint/utils": "8.43.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.42.0",
|
"@typescript-eslint/visitor-keys": "8.43.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^7.0.0",
|
"ignore": "^7.0.0",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@@ -3301,7 +3356,7 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/parser": "^8.42.0",
|
"@typescript-eslint/parser": "^8.43.0",
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <6.0.0"
|
"typescript": ">=4.8.4 <6.0.0"
|
||||||
}
|
}
|
||||||
@@ -3317,16 +3372,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz",
|
||||||
"integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==",
|
"integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.42.0",
|
"@typescript-eslint/scope-manager": "8.43.0",
|
||||||
"@typescript-eslint/types": "8.42.0",
|
"@typescript-eslint/types": "8.43.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.42.0",
|
"@typescript-eslint/typescript-estree": "8.43.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.42.0",
|
"@typescript-eslint/visitor-keys": "8.43.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3342,14 +3397,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/project-service": {
|
"node_modules/@typescript-eslint/project-service": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz",
|
||||||
"integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==",
|
"integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/tsconfig-utils": "^8.42.0",
|
"@typescript-eslint/tsconfig-utils": "^8.43.0",
|
||||||
"@typescript-eslint/types": "^8.42.0",
|
"@typescript-eslint/types": "^8.43.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3364,14 +3419,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz",
|
||||||
"integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==",
|
"integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.42.0",
|
"@typescript-eslint/types": "8.43.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.42.0"
|
"@typescript-eslint/visitor-keys": "8.43.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -3382,9 +3437,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz",
|
||||||
"integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==",
|
"integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3399,15 +3454,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz",
|
||||||
"integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==",
|
"integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.42.0",
|
"@typescript-eslint/types": "8.43.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.42.0",
|
"@typescript-eslint/typescript-estree": "8.43.0",
|
||||||
"@typescript-eslint/utils": "8.42.0",
|
"@typescript-eslint/utils": "8.43.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.1.0"
|
"ts-api-utils": "^2.1.0"
|
||||||
},
|
},
|
||||||
@@ -3424,9 +3479,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz",
|
||||||
"integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
|
"integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3438,16 +3493,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz",
|
||||||
"integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==",
|
"integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/project-service": "8.42.0",
|
"@typescript-eslint/project-service": "8.43.0",
|
||||||
"@typescript-eslint/tsconfig-utils": "8.42.0",
|
"@typescript-eslint/tsconfig-utils": "8.43.0",
|
||||||
"@typescript-eslint/types": "8.42.0",
|
"@typescript-eslint/types": "8.43.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.42.0",
|
"@typescript-eslint/visitor-keys": "8.43.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@@ -3467,16 +3522,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz",
|
||||||
"integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==",
|
"integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.7.0",
|
"@eslint-community/eslint-utils": "^4.7.0",
|
||||||
"@typescript-eslint/scope-manager": "8.42.0",
|
"@typescript-eslint/scope-manager": "8.43.0",
|
||||||
"@typescript-eslint/types": "8.42.0",
|
"@typescript-eslint/types": "8.43.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.42.0"
|
"@typescript-eslint/typescript-estree": "8.43.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -3491,13 +3546,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz",
|
||||||
"integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==",
|
"integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.42.0",
|
"@typescript-eslint/types": "8.43.0",
|
||||||
"eslint-visitor-keys": "^4.2.1"
|
"eslint-visitor-keys": "^4.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3779,6 +3834,7 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz",
|
||||||
"integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==",
|
"integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
},
|
},
|
||||||
@@ -4246,20 +4302,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cacheable": {
|
"node_modules/cacheable": {
|
||||||
"version": "1.10.3",
|
"version": "1.10.4",
|
||||||
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.3.tgz",
|
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.4.tgz",
|
||||||
"integrity": "sha512-M6p10iJ/VT0wT7TLIGUnm958oVrU2cUK8pQAVU21Zu7h8rbk/PeRtRWrvHJBql97Bhzk3g1N6+2VKC+Rjxna9Q==",
|
"integrity": "sha512-Gd7ccIUkZ9TE2odLQVS+PDjIvQCdJKUlLdJRVvZu0aipj07Qfx+XIej7hhDrKGGoIxV5m5fT/kOJNJPQhQneRg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hookified": "^1.10.0",
|
"hookified": "^1.11.0",
|
||||||
"keyv": "^5.4.0"
|
"keyv": "^5.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cacheable/node_modules/keyv": {
|
"node_modules/cacheable/node_modules/keyv": {
|
||||||
"version": "5.4.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.0.tgz",
|
||||||
"integrity": "sha512-TMckyVjEoacG5IteUpUrOBsFORtheqziVyyY2dLUwg1jwTb8u48LX4TgmtogkNl9Y9unaEJ1luj10fGyjMGFOQ==",
|
"integrity": "sha512-QG7qR2tijh1ftOvClut4YKKg1iW6cx3GZsKoGyJPxHkGWK9oJhG9P3j5deP0QQOGDowBMVQFaP+Vm4NpGYvmIQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5770,19 +5826,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.34.0",
|
"version": "9.35.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz",
|
||||||
"integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
|
"integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
"@eslint/config-array": "^0.21.0",
|
"@eslint/config-array": "^0.21.0",
|
||||||
"@eslint/config-helpers": "^0.3.1",
|
"@eslint/config-helpers": "^0.3.1",
|
||||||
"@eslint/core": "^0.15.2",
|
"@eslint/core": "^0.15.2",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "9.34.0",
|
"@eslint/js": "9.35.0",
|
||||||
"@eslint/plugin-kit": "^0.3.5",
|
"@eslint/plugin-kit": "^0.3.5",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
@@ -6811,9 +6867,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/gl-matrix": {
|
"node_modules/gl-matrix": {
|
||||||
"version": "3.4.3",
|
"version": "3.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz",
|
||||||
"integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
|
"integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "5.0.15",
|
"version": "5.0.15",
|
||||||
@@ -7235,9 +7292,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hookified": {
|
"node_modules/hookified": {
|
||||||
"version": "1.11.0",
|
"version": "1.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/hookified/-/hookified-1.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/hookified/-/hookified-1.12.0.tgz",
|
||||||
"integrity": "sha512-aDdIN3GyU5I6wextPplYdfmWCo+aLmjjVbntmX6HLD5RCi/xKsivYEBhnRD+d9224zFf008ZpLMPlWF0ZodYZw==",
|
"integrity": "sha512-hMr1Y9TCLshScrBbV2QxJ9BROddxZ12MX9KsCtuGGy/3SmmN5H1PllKerrVlSotur9dlE8hmUKAOSa3WDzsZmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -7568,14 +7625,6 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/invariant": {
|
|
||||||
"version": "2.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
|
||||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-array-buffer": {
|
"node_modules/is-array-buffer": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||||
@@ -9058,9 +9107,9 @@
|
|||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/maplibre-gl": {
|
"node_modules/maplibre-gl": {
|
||||||
"version": "5.6.2",
|
"version": "5.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.7.1.tgz",
|
||||||
"integrity": "sha512-SEqYThhUCFf6Lm0TckpgpKnto5u4JsdPYdFJb6g12VtuaFsm3nYdBO+fOmnUYddc8dXihgoGnuXvPPooUcRv5w==",
|
"integrity": "sha512-iCOQB6W/EGgQx8aU4SyfU5a5/GR2E+ELF92NMsqYfs3x+vnY+8mARmz4gor6XZHCz3tv19mnotVDRlRTMNKyGw==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mapbox/geojson-rewind": "^0.5.2",
|
"@mapbox/geojson-rewind": "^0.5.2",
|
||||||
@@ -9077,7 +9126,7 @@
|
|||||||
"@types/supercluster": "^7.1.3",
|
"@types/supercluster": "^7.1.3",
|
||||||
"earcut": "^3.0.2",
|
"earcut": "^3.0.2",
|
||||||
"geojson-vt": "^4.0.2",
|
"geojson-vt": "^4.0.2",
|
||||||
"gl-matrix": "^3.4.3",
|
"gl-matrix": "^3.4.4",
|
||||||
"kdbush": "^4.0.2",
|
"kdbush": "^4.0.2",
|
||||||
"murmurhash-js": "^1.0.0",
|
"murmurhash-js": "^1.0.0",
|
||||||
"pbf": "^4.0.1",
|
"pbf": "^4.0.1",
|
||||||
@@ -10898,21 +10947,6 @@
|
|||||||
"react-dom": ">=16.8"
|
"react-dom": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-sortable-hoc": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.2.0",
|
|
||||||
"invariant": "^2.2.4",
|
|
||||||
"prop-types": "^15.5.7"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"prop-types": "^15.5.7",
|
|
||||||
"react": "^16.3.0 || ^17.0.0",
|
|
||||||
"react-dom": "^16.3.0 || ^17.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/reactcss": {
|
"node_modules/reactcss": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||||
@@ -11355,9 +11389,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.92.0",
|
"version": "1.92.1",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.92.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.92.1.tgz",
|
||||||
"integrity": "sha512-KDNI0BxgIRDAfJgzNm5wuy+4yOCIZyrUbjSpiU/JItfih+KGXAVefKL53MTml054MmBA3DDKIBMSI/7XLxZJ3A==",
|
"integrity": "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -11973,9 +12007,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint": {
|
"node_modules/stylelint": {
|
||||||
"version": "16.23.1",
|
"version": "16.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.24.0.tgz",
|
||||||
"integrity": "sha512-dNvDTsKV1U2YtiUDfe9d2gp902veFeo3ecCWdGlmLm2WFrAV0+L5LoOj/qHSBABQwMsZPJwfC4bf39mQm1S5zw==",
|
"integrity": "sha512-7ksgz3zJaSbTUGr/ujMXvLVKdDhLbGl3R/3arNudH7z88+XZZGNLMTepsY28WlnvEFcuOmUe7fg40Q3lfhOfSQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -12002,7 +12036,7 @@
|
|||||||
"debug": "^4.4.1",
|
"debug": "^4.4.1",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"fastest-levenshtein": "^1.0.16",
|
"fastest-levenshtein": "^1.0.16",
|
||||||
"file-entry-cache": "^10.1.3",
|
"file-entry-cache": "^10.1.4",
|
||||||
"global-modules": "^2.0.0",
|
"global-modules": "^2.0.0",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"globjoin": "^0.1.4",
|
"globjoin": "^0.1.4",
|
||||||
@@ -12137,25 +12171,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint/node_modules/file-entry-cache": {
|
"node_modules/stylelint/node_modules/file-entry-cache": {
|
||||||
"version": "10.1.3",
|
"version": "10.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.4.tgz",
|
||||||
"integrity": "sha512-D+w75Ub8T55yor7fPgN06rkCAUbAYw2vpxJmmjv/GDAcvCnv9g7IvHhIZoxzRZThrXPFI2maeY24pPbtyYU7Lg==",
|
"integrity": "sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flat-cache": "^6.1.12"
|
"flat-cache": "^6.1.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint/node_modules/flat-cache": {
|
"node_modules/stylelint/node_modules/flat-cache": {
|
||||||
"version": "6.1.12",
|
"version": "6.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.13.tgz",
|
||||||
"integrity": "sha512-U+HqqpZPPXP5d24bWuRzjGqVqUcw64k4nZAbruniDwdRg0H10tvN7H6ku1tjhA4rg5B9GS3siEvwO2qjJJ6f8Q==",
|
"integrity": "sha512-gmtS2PaUjSPa4zjObEIn4WWliKyZzYljgxODBfxugpK6q6HU9ClXzgCJ+nlcPKY9Bt090ypTOLIFWkV0jbKFjw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cacheable": "^1.10.3",
|
"cacheable": "^1.10.4",
|
||||||
"flatted": "^3.3.3",
|
"flatted": "^3.3.3",
|
||||||
"hookified": "^1.10.0"
|
"hookified": "^1.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint/node_modules/ignore": {
|
"node_modules/stylelint/node_modules/ignore": {
|
||||||
@@ -12485,14 +12519,14 @@
|
|||||||
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
|
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.14",
|
"version": "0.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.5.0",
|
||||||
"picomatch": "^4.0.2"
|
"picomatch": "^4.0.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
@@ -12771,16 +12805,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript-eslint": {
|
"node_modules/typescript-eslint": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.43.0.tgz",
|
||||||
"integrity": "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg==",
|
"integrity": "sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "8.42.0",
|
"@typescript-eslint/eslint-plugin": "8.43.0",
|
||||||
"@typescript-eslint/parser": "8.42.0",
|
"@typescript-eslint/parser": "8.43.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.42.0",
|
"@typescript-eslint/typescript-estree": "8.43.0",
|
||||||
"@typescript-eslint/utils": "8.42.0"
|
"@typescript-eslint/utils": "8.43.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@@ -12945,9 +12979,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "11.1.0",
|
"version": "13.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||||
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://github.com/sponsors/broofa",
|
"https://github.com/sponsors/broofa",
|
||||||
@@ -12955,7 +12989,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"uuid": "dist/esm/bin/uuid"
|
"uuid": "dist-node/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/value-or-function": {
|
"node_modules/value-or-function": {
|
||||||
@@ -13063,9 +13097,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.1.4",
|
"version": "7.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
|
||||||
"integrity": "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==",
|
"integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -13074,7 +13108,7 @@
|
|||||||
"picomatch": "^4.0.3",
|
"picomatch": "^4.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rollup": "^4.43.0",
|
"rollup": "^4.43.0",
|
||||||
"tinyglobby": "^0.2.14"
|
"tinyglobby": "^0.2.15"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
|
|||||||
+16
-13
@@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "maputnik",
|
"name": "maputnik",
|
||||||
"version": "2.1.1",
|
"version": "3.0.0",
|
||||||
"description": "A MapLibre GL visual style editor",
|
"description": "A MapLibre GL visual style editor",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "''",
|
"main": "''",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
"build": "tsc && vite build --base=/maputnik/",
|
"build": "tsc && vite build --mode=production",
|
||||||
"build-desktop": "tsc && vite build --base=/ && cd desktop && make",
|
"build-desktop": "tsc && vite build --mode=desktop && cd desktop && make",
|
||||||
|
"build-linux": "tsc && vite build --mode=desktop && cd desktop && make bin/linux/maputnik",
|
||||||
"i18n:refresh": "i18next 'src/**/*.{ts,tsx,js,jsx}'",
|
"i18n:refresh": "i18next 'src/**/*.{ts,tsx,js,jsx}'",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"test": "cypress run",
|
"test": "cypress run",
|
||||||
@@ -23,6 +24,9 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/maplibre/maputnik#readme",
|
"homepage": "https://github.com/maplibre/maputnik#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@dnd-kit/core": "^6.3.1",
|
||||||
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "^0.3.0",
|
"@mapbox/mapbox-gl-rtl-text": "^0.3.0",
|
||||||
"@maplibre/maplibre-gl-geocoder": "^1.9.0",
|
"@maplibre/maplibre-gl-geocoder": "^1.9.0",
|
||||||
"@maplibre/maplibre-gl-inspect": "^1.7.1",
|
"@maplibre/maplibre-gl-inspect": "^1.7.1",
|
||||||
@@ -36,6 +40,7 @@
|
|||||||
"codemirror": "^5.65.20",
|
"codemirror": "^5.65.20",
|
||||||
"color": "^5.0.0",
|
"color": "^5.0.0",
|
||||||
"detect-browser": "^5.3.0",
|
"detect-browser": "^5.3.0",
|
||||||
|
"downshift": "^9.0.10",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18next": "^25.5.2",
|
"i18next": "^25.5.2",
|
||||||
@@ -50,7 +55,7 @@
|
|||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"maplibre-gl": "^5.6.2",
|
"maplibre-gl": "^5.7.1",
|
||||||
"maputnik-design": "github:maputnik/design#172b06c",
|
"maputnik-design": "github:maputnik/design#172b06c",
|
||||||
"ol": "^10.6.1",
|
"ol": "^10.6.1",
|
||||||
"ol-mapbox-style": "^13.1.0",
|
"ol-mapbox-style": "^13.1.0",
|
||||||
@@ -60,7 +65,6 @@
|
|||||||
"react-accessible-accordion": "^5.0.1",
|
"react-accessible-accordion": "^5.0.1",
|
||||||
"react-aria-menubutton": "^7.0.3",
|
"react-aria-menubutton": "^7.0.3",
|
||||||
"react-aria-modal": "^5.0.2",
|
"react-aria-modal": "^5.0.2",
|
||||||
"downshift": "^9.0.10",
|
|
||||||
"react-collapse": "^5.1.1",
|
"react-collapse": "^5.1.1",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@@ -68,7 +72,6 @@
|
|||||||
"react-i18next": "^15.7.3",
|
"react-i18next": "^15.7.3",
|
||||||
"react-icon-base": "^2.1.2",
|
"react-icon-base": "^2.1.2",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-sortable-hoc": "^2.0.0",
|
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"string-hash": "^1.1.3",
|
"string-hash": "^1.1.3",
|
||||||
@@ -93,7 +96,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/code-coverage": "^3.14.6",
|
"@cypress/code-coverage": "^3.14.6",
|
||||||
"@eslint/js": "^9.34.0",
|
"@eslint/js": "^9.35.0",
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
"@rollup/plugin-replace": "^6.0.2",
|
"@rollup/plugin-replace": "^6.0.2",
|
||||||
"@shellygo/cypress-test-utils": "^6.0.1",
|
"@shellygo/cypress-test-utils": "^6.0.1",
|
||||||
@@ -125,7 +128,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cypress": "^15.1.0",
|
"cypress": "^15.1.0",
|
||||||
"cypress-plugin-tab": "^1.0.5",
|
"cypress-plugin-tab": "^1.0.5",
|
||||||
"eslint": "^9.34.0",
|
"eslint": "^9.35.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
@@ -134,14 +137,14 @@
|
|||||||
"istanbul-lib-coverage": "^3.2.2",
|
"istanbul-lib-coverage": "^3.2.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react-hot-loader": "^4.13.1",
|
"react-hot-loader": "^4.13.1",
|
||||||
"sass": "^1.92.0",
|
"sass": "^1.92.1",
|
||||||
"stylelint": "^16.23.1",
|
"stylelint": "^16.24.0",
|
||||||
"stylelint-config-recommended-scss": "^16.0.0",
|
"stylelint-config-recommended-scss": "^16.0.0",
|
||||||
"stylelint-scss": "^6.12.1",
|
"stylelint-scss": "^6.12.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.42.0",
|
"typescript-eslint": "^8.43.0",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^13.0.0",
|
||||||
"vite": "^7.1.4",
|
"vite": "^7.1.5",
|
||||||
"vite-plugin-istanbul": "^7.1.0"
|
"vite-plugin-istanbul": "^7.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-102
@@ -27,17 +27,14 @@ import ModalDebug from './ModalDebug'
|
|||||||
|
|
||||||
import {downloadGlyphsMetadata, downloadSpriteMetadata} from '../libs/metadata'
|
import {downloadGlyphsMetadata, downloadSpriteMetadata} from '../libs/metadata'
|
||||||
import style from '../libs/style'
|
import style from '../libs/style'
|
||||||
import { initialStyleUrl, loadStyleUrl, removeStyleQuerystring } from '../libs/urlopen'
|
|
||||||
import { undoMessages, redoMessages } from '../libs/diffmessage'
|
import { undoMessages, redoMessages } from '../libs/diffmessage'
|
||||||
import { StyleStore } from '../libs/stylestore'
|
import { createStyleStore, type IStyleStore } from '../libs/store/style-store-factory'
|
||||||
import { ApiStyleStore } from '../libs/apistore'
|
|
||||||
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 Debug from '../libs/debug'
|
|
||||||
import { SortEnd } from 'react-sortable-hoc';
|
|
||||||
import { MapOptions } from 'maplibre-gl';
|
import { MapOptions } from 'maplibre-gl';
|
||||||
|
import { OnStyleChangedOpts, StyleSpecificationWithId } from '../libs/definitions'
|
||||||
|
|
||||||
// Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed.
|
// Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed.
|
||||||
window.Buffer = buffer.Buffer;
|
window.Buffer = buffer.Buffer;
|
||||||
@@ -83,12 +80,6 @@ function updateRootSpec(spec: any, fieldName: string, newValues: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type OnStyleChangedOpts = {
|
|
||||||
save?: boolean
|
|
||||||
addRevision?: boolean
|
|
||||||
initialLoad?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type MappedErrors = {
|
type MappedErrors = {
|
||||||
message: string
|
message: string
|
||||||
parsed?: {
|
parsed?: {
|
||||||
@@ -104,7 +95,7 @@ type MappedErrors = {
|
|||||||
type AppState = {
|
type AppState = {
|
||||||
errors: MappedErrors[],
|
errors: MappedErrors[],
|
||||||
infos: string[],
|
infos: string[],
|
||||||
mapStyle: StyleSpecification & {id: string},
|
mapStyle: StyleSpecificationWithId,
|
||||||
dirtyMapStyle?: StyleSpecification,
|
dirtyMapStyle?: StyleSpecification,
|
||||||
selectedLayerIndex: number,
|
selectedLayerIndex: number,
|
||||||
selectedLayerOriginalId?: string,
|
selectedLayerOriginalId?: string,
|
||||||
@@ -140,25 +131,56 @@ type AppState = {
|
|||||||
|
|
||||||
export default class App extends React.Component<any, AppState> {
|
export default class App extends React.Component<any, AppState> {
|
||||||
revisionStore: RevisionStore;
|
revisionStore: RevisionStore;
|
||||||
styleStore: StyleStore | ApiStyleStore;
|
styleStore: IStyleStore | null = null;
|
||||||
layerWatcher: LayerWatcher;
|
layerWatcher: LayerWatcher;
|
||||||
|
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.revisionStore = new RevisionStore()
|
this.revisionStore = new RevisionStore();
|
||||||
const params = new URLSearchParams(window.location.search.substring(1))
|
this.configureKeyboardShortcuts();
|
||||||
let port = params.get("localport")
|
|
||||||
if (port == null && (window.location.port !== "80" && window.location.port !== "443")) {
|
this.state = {
|
||||||
port = window.location.port
|
errors: [],
|
||||||
|
infos: [],
|
||||||
|
mapStyle: style.emptyStyle,
|
||||||
|
selectedLayerIndex: 0,
|
||||||
|
sources: {},
|
||||||
|
vectorLayers: {},
|
||||||
|
mapState: "map",
|
||||||
|
spec: latest,
|
||||||
|
mapView: {
|
||||||
|
zoom: 0,
|
||||||
|
center: {
|
||||||
|
lng: 0,
|
||||||
|
lat: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isOpen: {
|
||||||
|
settings: false,
|
||||||
|
sources: false,
|
||||||
|
open: false,
|
||||||
|
shortcuts: false,
|
||||||
|
export: false,
|
||||||
|
debug: false,
|
||||||
|
},
|
||||||
|
maplibreGlDebugOptions: {
|
||||||
|
showTileBoundaries: false,
|
||||||
|
showCollisionBoxes: false,
|
||||||
|
showOverdrawInspector: false,
|
||||||
|
},
|
||||||
|
openlayersDebugOptions: {
|
||||||
|
debugToolbox: false,
|
||||||
|
},
|
||||||
|
fileHandle: null,
|
||||||
}
|
}
|
||||||
this.styleStore = new ApiStyleStore({
|
|
||||||
onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, {save: false}),
|
this.layerWatcher = new LayerWatcher({
|
||||||
port: port,
|
onVectorLayersChange: v => this.setState({ vectorLayers: v })
|
||||||
host: params.get("localhost")
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
configureKeyboardShortcuts = () => {
|
||||||
const shortcuts = [
|
const shortcuts = [
|
||||||
{
|
{
|
||||||
key: "?",
|
key: "?",
|
||||||
@@ -228,74 +250,6 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleUrl = initialStyleUrl()
|
|
||||||
if(styleUrl && window.confirm("Load style from URL: " + styleUrl + " and discard current changes?")) {
|
|
||||||
this.styleStore = new StyleStore()
|
|
||||||
loadStyleUrl(styleUrl, mapStyle => this.onStyleChanged(mapStyle))
|
|
||||||
removeStyleQuerystring()
|
|
||||||
} else {
|
|
||||||
if(styleUrl) {
|
|
||||||
removeStyleQuerystring()
|
|
||||||
}
|
|
||||||
this.styleStore.init(err => {
|
|
||||||
if(err) {
|
|
||||||
console.log('Falling back to local storage for storing styles')
|
|
||||||
this.styleStore = new StyleStore()
|
|
||||||
}
|
|
||||||
this.styleStore.latestStyle(mapStyle => this.onStyleChanged(mapStyle, {initialLoad: true}))
|
|
||||||
|
|
||||||
if(Debug.enabled()) {
|
|
||||||
Debug.set("maputnik", "styleStore", this.styleStore);
|
|
||||||
Debug.set("maputnik", "revisionStore", this.revisionStore);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Debug.enabled()) {
|
|
||||||
Debug.set("maputnik", "revisionStore", this.revisionStore);
|
|
||||||
Debug.set("maputnik", "styleStore", this.styleStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
errors: [],
|
|
||||||
infos: [],
|
|
||||||
mapStyle: style.emptyStyle,
|
|
||||||
selectedLayerIndex: 0,
|
|
||||||
sources: {},
|
|
||||||
vectorLayers: {},
|
|
||||||
mapState: "map",
|
|
||||||
spec: latest,
|
|
||||||
mapView: {
|
|
||||||
zoom: 0,
|
|
||||||
center: {
|
|
||||||
lng: 0,
|
|
||||||
lat: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isOpen: {
|
|
||||||
settings: false,
|
|
||||||
sources: false,
|
|
||||||
open: false,
|
|
||||||
shortcuts: false,
|
|
||||||
export: false,
|
|
||||||
// TODO: Disabled for now, this should be opened on the Nth visit to the editor
|
|
||||||
debug: false,
|
|
||||||
},
|
|
||||||
maplibreGlDebugOptions: {
|
|
||||||
showTileBoundaries: false,
|
|
||||||
showCollisionBoxes: false,
|
|
||||||
showOverdrawInspector: false,
|
|
||||||
},
|
|
||||||
openlayersDebugOptions: {
|
|
||||||
debugToolbox: false,
|
|
||||||
},
|
|
||||||
fileHandle: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.layerWatcher = new LayerWatcher({
|
|
||||||
onVectorLayersChange: v => this.setState({ vectorLayers: v })
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress = (e: KeyboardEvent) => {
|
handleKeyPress = (e: KeyboardEvent) => {
|
||||||
@@ -321,7 +275,8 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
async componentDidMount() {
|
||||||
|
this.styleStore = await createStyleStore((mapStyle, opts) => this.onStyleChanged(mapStyle, opts));
|
||||||
window.addEventListener("keydown", this.handleKeyPress);
|
window.addEventListener("keydown", this.handleKeyPress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,8 +284,8 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
window.removeEventListener("keydown", this.handleKeyPress);
|
window.removeEventListener("keydown", this.handleKeyPress);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveStyle(snapshotStyle: StyleSpecification & {id: string}) {
|
saveStyle(snapshotStyle: StyleSpecificationWithId) {
|
||||||
this.styleStore.save(snapshotStyle)
|
this.styleStore?.save(snapshotStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFonts(urlTemplate: string) {
|
updateFonts(urlTemplate: string) {
|
||||||
@@ -371,7 +326,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
this.onStyleChanged(changedStyle)
|
this.onStyleChanged(changedStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
onStyleChanged = (newStyle: StyleSpecification & {id: string}, opts: OnStyleChangedOpts={}) => {
|
onStyleChanged = (newStyle: StyleSpecificationWithId, opts: OnStyleChangedOpts={}): void => {
|
||||||
opts = {
|
opts = {
|
||||||
save: true,
|
save: true,
|
||||||
addRevision: true,
|
addRevision: true,
|
||||||
@@ -480,7 +435,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
dirtyMapStyle = cloneDeep(newStyle);
|
dirtyMapStyle = cloneDeep(newStyle);
|
||||||
|
|
||||||
errors.forEach(error => {
|
for (const error of errors) {
|
||||||
const {message} = error;
|
const {message} = error;
|
||||||
if (message) {
|
if (message) {
|
||||||
try {
|
try {
|
||||||
@@ -490,10 +445,10 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
unset(dirtyMapStyle, unsetPath);
|
unset(dirtyMapStyle, unsetPath);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.warn(err);
|
console.warn(message + " " + err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
|
if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
|
||||||
@@ -507,7 +462,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
this.revisionStore.addRevision(newStyle);
|
this.revisionStore.addRevision(newStyle);
|
||||||
}
|
}
|
||||||
if (opts.save) {
|
if (opts.save) {
|
||||||
this.saveStyle(newStyle as StyleSpecification & {id: string});
|
this.saveStyle(newStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -540,7 +495,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMoveLayer = (move: SortEnd) => {
|
onMoveLayer = (move: {oldIndex: number; newIndex: number}) => {
|
||||||
let { oldIndex, newIndex } = move;
|
let { oldIndex, newIndex } = move;
|
||||||
let layers = this.state.mapStyle.layers;
|
let layers = this.state.mapStyle.layers;
|
||||||
oldIndex = clamp(oldIndex, 0, layers.length-1);
|
oldIndex = clamp(oldIndex, 0, layers.length-1);
|
||||||
@@ -620,7 +575,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
}, this.setStateInUrl);
|
}, this.setStateInUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDefaultValues = (styleObj: StyleSpecification & {id: string}) => {
|
setDefaultValues = (styleObj: StyleSpecificationWithId) => {
|
||||||
const metadata: {[key: string]: string} = styleObj.metadata || {} as any
|
const metadata: {[key: string]: string} = styleObj.metadata || {} as any
|
||||||
if(metadata['maputnik:renderer'] === undefined) {
|
if(metadata['maputnik:renderer'] === undefined) {
|
||||||
const changedStyle = {
|
const changedStyle = {
|
||||||
@@ -636,7 +591,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openStyle = (styleObj: StyleSpecification & {id: string}, fileHandle: FileSystemFileHandle | null) => {
|
openStyle = (styleObj: StyleSpecificationWithId, fileHandle: FileSystemFileHandle | null) => {
|
||||||
this.setState({fileHandle: fileHandle});
|
this.setState({fileHandle: fileHandle});
|
||||||
styleObj = this.setDefaultValues(styleObj)
|
styleObj = this.setDefaultValues(styleObj)
|
||||||
this.onStyleChanged(styleObj)
|
this.onStyleChanged(styleObj)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import pkgJson from '../../package.json'
|
|||||||
import maputnikLogo from 'maputnik-design/logos/logo-color.svg?inline'
|
import maputnikLogo from 'maputnik-design/logos/logo-color.svg?inline'
|
||||||
import { withTranslation, WithTranslation } from 'react-i18next';
|
import { withTranslation, WithTranslation } from 'react-i18next';
|
||||||
import { supportedLanguages } from '../i18n';
|
import { supportedLanguages } from '../i18n';
|
||||||
|
import type { OnStyleChangedCallback } from '../libs/definitions';
|
||||||
|
|
||||||
// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
|
// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
|
||||||
const browser = detect();
|
const browser = detect();
|
||||||
@@ -93,9 +94,9 @@ export type MapState = "map" | "inspect" | "filter-achromatopsia" | "filter-deut
|
|||||||
type AppToolbarInternalProps = {
|
type AppToolbarInternalProps = {
|
||||||
mapStyle: object
|
mapStyle: object
|
||||||
inspectModeEnabled: boolean
|
inspectModeEnabled: boolean
|
||||||
onStyleChanged(...args: unknown[]): unknown
|
onStyleChanged: OnStyleChangedCallback
|
||||||
// A new style has been uploaded
|
// A new style has been uploaded
|
||||||
onStyleOpen(...args: unknown[]): unknown
|
onStyleOpen: OnStyleChangedCallback
|
||||||
// A dict of source id's and the available source layers
|
// A dict of source id's and the available source layers
|
||||||
sources: object
|
sources: object
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
|
|||||||
@@ -13,28 +13,30 @@ type FieldSourceInternalProps = {
|
|||||||
error?: {message: string}
|
error?: {message: string}
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
const FieldSourceInternal: React.FC<FieldSourceInternalProps> = (props) => {
|
const FieldSourceInternal: React.FC<FieldSourceInternalProps> = ({
|
||||||
const t = props.t;
|
onChange = () => {},
|
||||||
|
sourceIds = [],
|
||||||
|
wdKey,
|
||||||
|
value,
|
||||||
|
error,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Block
|
<Block
|
||||||
label={t('Source')}
|
label={t('Source')}
|
||||||
fieldSpec={latest.layer.source}
|
fieldSpec={latest.layer.source}
|
||||||
error={props.error}
|
error={error}
|
||||||
data-wd-key={props.wdKey}
|
data-wd-key={wdKey}
|
||||||
>
|
>
|
||||||
<InputAutocomplete
|
<InputAutocomplete
|
||||||
value={props.value}
|
value={value}
|
||||||
onChange={props.onChange}
|
onChange={onChange}
|
||||||
options={props.sourceIds?.map((src) => [src, src])}
|
options={sourceIds?.map((src) => [src, src])}
|
||||||
/>
|
/>
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
FieldSourceInternal.defaultProps = {
|
|
||||||
onChange: () => {},
|
|
||||||
sourceIds: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const FieldSource = withTranslation()(FieldSourceInternal);
|
const FieldSource = withTranslation()(FieldSourceInternal);
|
||||||
export default FieldSource;
|
export default FieldSource;
|
||||||
|
|||||||
@@ -9,33 +9,31 @@ type FieldSourceLayerInternalProps = {
|
|||||||
value?: string
|
value?: string
|
||||||
onChange?(...args: unknown[]): unknown
|
onChange?(...args: unknown[]): unknown
|
||||||
sourceLayerIds?: unknown[]
|
sourceLayerIds?: unknown[]
|
||||||
isFixed?: boolean
|
|
||||||
error?: {message: string}
|
error?: {message: string}
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
const FieldSourceLayerInternal: React.FC<FieldSourceLayerInternalProps> = (props) => {
|
const FieldSourceLayerInternal: React.FC<FieldSourceLayerInternalProps> = ({
|
||||||
const t = props.t;
|
onChange = () => {},
|
||||||
|
sourceLayerIds = [],
|
||||||
|
value,
|
||||||
|
error,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Block
|
<Block
|
||||||
label={t('Source Layer')}
|
label={t('Source Layer')}
|
||||||
fieldSpec={latest.layer['source-layer']}
|
fieldSpec={latest.layer['source-layer']}
|
||||||
data-wd-key="layer-source-layer"
|
data-wd-key="layer-source-layer"
|
||||||
error={props.error}
|
error={error}
|
||||||
>
|
>
|
||||||
<InputAutocomplete
|
<InputAutocomplete
|
||||||
value={props.value}
|
value={value}
|
||||||
onChange={props.onChange}
|
onChange={onChange}
|
||||||
options={props.sourceLayerIds?.map((l) => [l, l])}
|
options={sourceLayerIds?.map((l) => [l, l])}
|
||||||
/>
|
/>
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
FieldSourceLayerInternal.defaultProps = {
|
|
||||||
onChange: () => {},
|
|
||||||
sourceLayerIds: [],
|
|
||||||
isFixed: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const FieldSourceLayer = withTranslation()(FieldSourceLayerInternal);
|
const FieldSourceLayer = withTranslation()(FieldSourceLayerInternal);
|
||||||
export default FieldSourceLayer;
|
export default FieldSourceLayer;
|
||||||
|
|||||||
@@ -14,32 +14,34 @@ type FieldTypeInternalProps = {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
const FieldTypeInternal: React.FC<FieldTypeInternalProps> = (props) => {
|
const FieldTypeInternal: React.FC<FieldTypeInternalProps> = ({
|
||||||
const t = props.t;
|
t,
|
||||||
|
value,
|
||||||
|
wdKey,
|
||||||
|
onChange,
|
||||||
|
error,
|
||||||
|
disabled = false
|
||||||
|
}) => {
|
||||||
const layerstypes: [string, string][] = Object.keys(v8.layer.type.values || {}).map(v => [v, startCase(v.replace(/-/g, ' '))]);
|
const layerstypes: [string, string][] = Object.keys(v8.layer.type.values || {}).map(v => [v, startCase(v.replace(/-/g, ' '))]);
|
||||||
return (
|
return (
|
||||||
<Block label={t('Type')} fieldSpec={v8.layer.type}
|
<Block label={t('Type')} fieldSpec={v8.layer.type}
|
||||||
data-wd-key={props.wdKey}
|
data-wd-key={wdKey}
|
||||||
error={props.error}
|
error={error}
|
||||||
>
|
>
|
||||||
{props.disabled && (
|
{disabled && (
|
||||||
<InputString value={props.value} disabled={true} />
|
<InputString value={value} disabled={true} />
|
||||||
)}
|
)}
|
||||||
{!props.disabled && (
|
{!disabled && (
|
||||||
<InputSelect
|
<InputSelect
|
||||||
options={layerstypes}
|
options={layerstypes}
|
||||||
onChange={props.onChange}
|
onChange={onChange}
|
||||||
value={props.value}
|
value={value}
|
||||||
data-wd-key={props.wdKey + '.select'}
|
data-wd-key={wdKey + '.select'}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Block>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
FieldTypeInternal.defaultProps = {
|
|
||||||
disabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const FieldType = withTranslation()(FieldTypeInternal);
|
const FieldType = withTranslation()(FieldTypeInternal);
|
||||||
export default FieldType;
|
export default FieldType;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {mdiTableRowPlusAfter} from '@mdi/js';
|
import {mdiTableRowPlusAfter} from '@mdi/js';
|
||||||
import {isEqual} from 'lodash';
|
import {isEqual} from 'lodash';
|
||||||
import {ExpressionSpecification, LegacyFilterSpecification, StyleSpecification} from 'maplibre-gl'
|
import {ExpressionSpecification, LegacyFilterSpecification} from 'maplibre-gl'
|
||||||
import {latest, migrate, convertFilter} from '@maplibre/maplibre-gl-style-spec'
|
import {latest, migrate, convertFilter} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import {mdiFunctionVariant} from '@mdi/js';
|
import {mdiFunctionVariant} from '@mdi/js';
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ import InputButton from './InputButton'
|
|||||||
import Doc from './Doc'
|
import Doc from './Doc'
|
||||||
import ExpressionProperty from './_ExpressionProperty';
|
import ExpressionProperty from './_ExpressionProperty';
|
||||||
import { WithTranslation, withTranslation } from 'react-i18next';
|
import { WithTranslation, withTranslation } from 'react-i18next';
|
||||||
|
import type { StyleSpecificationWithId } from '../libs/definitions';
|
||||||
|
|
||||||
|
|
||||||
function combiningFilter(props: FilterEditorInternalProps): LegacyFilterSpecification | ExpressionSpecification {
|
function combiningFilter(props: FilterEditorInternalProps): LegacyFilterSpecification | ExpressionSpecification {
|
||||||
@@ -39,7 +40,7 @@ function migrateFilter(filter: LegacyFilterSpecification | ExpressionSpecificati
|
|||||||
return (migrate(createStyleFromFilter(filter) as any).layers[0] as any).filter;
|
return (migrate(createStyleFromFilter(filter) as any).layers[0] as any).filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStyleFromFilter(filter: LegacyFilterSpecification | ExpressionSpecification): StyleSpecification & {id: string} {
|
function createStyleFromFilter(filter: LegacyFilterSpecification | ExpressionSpecification): StyleSpecificationWithId {
|
||||||
return {
|
return {
|
||||||
"id": "tmp",
|
"id": "tmp",
|
||||||
"version": 8,
|
"version": 8,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {formatLayerId} from '../libs/format';
|
|||||||
import { WithTranslation, withTranslation } from 'react-i18next';
|
import { WithTranslation, withTranslation } from 'react-i18next';
|
||||||
import { TFunction } from 'i18next';
|
import { TFunction } from 'i18next';
|
||||||
import { NON_SOURCE_LAYERS } from '../libs/non-source-layers';
|
import { NON_SOURCE_LAYERS } from '../libs/non-source-layers';
|
||||||
|
import { OnMoveLayerCallback } from '../libs/definitions';
|
||||||
|
|
||||||
type MaputnikLayoutGroup = {
|
type MaputnikLayoutGroup = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -120,7 +121,7 @@ type LayerEditorInternalProps = {
|
|||||||
spec: object
|
spec: object
|
||||||
onLayerChanged(...args: unknown[]): unknown
|
onLayerChanged(...args: unknown[]): unknown
|
||||||
onLayerIdChange(...args: unknown[]): unknown
|
onLayerIdChange(...args: unknown[]): unknown
|
||||||
onMoveLayer(...args: unknown[]): unknown
|
onMoveLayer: OnMoveLayerCallback
|
||||||
onLayerDestroy(...args: unknown[]): unknown
|
onLayerDestroy(...args: unknown[]): unknown
|
||||||
onLayerCopy(...args: unknown[]): unknown
|
onLayerCopy(...args: unknown[]): unknown
|
||||||
onLayerVisibilityToggle(...args: unknown[]): unknown
|
onLayerVisibilityToggle(...args: unknown[]): unknown
|
||||||
@@ -322,30 +323,38 @@ class LayerEditorInternal extends React.Component<LayerEditorInternalProps, Laye
|
|||||||
|
|
||||||
const layout = this.props.layer.layout || {}
|
const layout = this.props.layer.layout || {}
|
||||||
|
|
||||||
const items: {[key: string]: {text: string, handler: () => void, disabled?: boolean}} = {
|
const items: {[key: string]: {
|
||||||
|
text: string,
|
||||||
|
handler: () => void,
|
||||||
|
disabled?: boolean,
|
||||||
|
wdKey?: string
|
||||||
|
}} = {
|
||||||
delete: {
|
delete: {
|
||||||
text: t("Delete"),
|
text: t("Delete"),
|
||||||
handler: () => this.props.onLayerDestroy(this.props.layerIndex)
|
handler: () => this.props.onLayerDestroy(this.props.layerIndex),
|
||||||
|
wdKey: "menu-delete-layer"
|
||||||
},
|
},
|
||||||
duplicate: {
|
duplicate: {
|
||||||
text: t("Duplicate"),
|
text: t("Duplicate"),
|
||||||
handler: () => this.props.onLayerCopy(this.props.layerIndex)
|
handler: () => this.props.onLayerCopy(this.props.layerIndex),
|
||||||
|
wdKey: "menu-duplicate-layer"
|
||||||
},
|
},
|
||||||
hide: {
|
hide: {
|
||||||
text: (layout.visibility === "none") ? t("Show") : t("Hide"),
|
text: (layout.visibility === "none") ? t("Show") : t("Hide"),
|
||||||
handler: () => this.props.onLayerVisibilityToggle(this.props.layerIndex)
|
handler: () => this.props.onLayerVisibilityToggle(this.props.layerIndex),
|
||||||
|
wdKey: "menu-hide-layer"
|
||||||
},
|
},
|
||||||
moveLayerUp: {
|
moveLayerUp: {
|
||||||
text: t("Move layer up"),
|
text: t("Move layer up"),
|
||||||
// Not actually used...
|
|
||||||
disabled: this.props.isFirstLayer,
|
disabled: this.props.isFirstLayer,
|
||||||
handler: () => this.moveLayer(-1)
|
handler: () => this.moveLayer(-1),
|
||||||
|
wdKey: "menu-move-layer-up"
|
||||||
},
|
},
|
||||||
moveLayerDown: {
|
moveLayerDown: {
|
||||||
text: t("Move layer down"),
|
text: t("Move layer down"),
|
||||||
// Not actually used...
|
|
||||||
disabled: this.props.isLastLayer,
|
disabled: this.props.isLastLayer,
|
||||||
handler: () => this.moveLayer(+1)
|
handler: () => this.moveLayer(+1),
|
||||||
|
wdKey: "menu-move-layer-down"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +391,7 @@ class LayerEditorInternal extends React.Component<LayerEditorInternalProps, Laye
|
|||||||
{Object.keys(items).map((id) => {
|
{Object.keys(items).map((id) => {
|
||||||
const item = items[id];
|
const item = items[id];
|
||||||
return <li key={id}>
|
return <li key={id}>
|
||||||
<MenuItem value={id} className='more-menu__menu__item'>
|
<MenuItem value={id} className='more-menu__menu__item' data-wd-key={item.wdKey}>
|
||||||
{item.text}
|
{item.text}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
import React, {type JSX} from 'react'
|
import React, {type JSX} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
|
import {
|
||||||
|
DndContext,
|
||||||
|
PointerSensor,
|
||||||
|
useSensor,
|
||||||
|
useSensors,
|
||||||
|
closestCenter,
|
||||||
|
DragEndEvent,
|
||||||
|
} from '@dnd-kit/core';
|
||||||
|
import {
|
||||||
|
SortableContext,
|
||||||
|
verticalListSortingStrategy,
|
||||||
|
} from '@dnd-kit/sortable';
|
||||||
|
|
||||||
import LayerListGroup from './LayerListGroup'
|
import LayerListGroup from './LayerListGroup'
|
||||||
import LayerListItem from './LayerListItem'
|
import LayerListItem from './LayerListItem'
|
||||||
import ModalAdd from './ModalAdd'
|
import ModalAdd from './ModalAdd'
|
||||||
|
|
||||||
import {SortEndHandler, SortableContainer} from 'react-sortable-hoc';
|
|
||||||
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 { WithTranslation, withTranslation } from 'react-i18next';
|
import { WithTranslation, withTranslation } from 'react-i18next';
|
||||||
|
import { OnMoveLayerCallback } from '../libs/definitions';
|
||||||
|
|
||||||
type LayerListContainerProps = {
|
type LayerListContainerProps = {
|
||||||
layers: LayerSpecification[]
|
layers: LayerSpecification[]
|
||||||
@@ -242,7 +254,6 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
|||||||
'maputnik-layer-list-item-group-last': idxInGroup == layers.length - 1 && layers.length > 1,
|
'maputnik-layer-list-item-group-last': idxInGroup == layers.length - 1 && layers.length > 1,
|
||||||
'maputnik-layer-list-item--error': !!layerError
|
'maputnik-layer-list-item--error': !!layerError
|
||||||
})}
|
})}
|
||||||
index={idx}
|
|
||||||
key={layer.key}
|
key={layer.key}
|
||||||
id={layer.key}
|
id={layer.key}
|
||||||
layerId={layer.id}
|
layerId={layer.id}
|
||||||
@@ -319,20 +330,35 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LayerListContainer = withTranslation()(LayerListContainerInternal);
|
const LayerListContainer = withTranslation()(LayerListContainerInternal);
|
||||||
const LayerListContainerSortable = SortableContainer((props: LayerListContainerProps) => <LayerListContainer {...props} />)
|
|
||||||
|
|
||||||
type LayerListProps = LayerListContainerProps & {
|
type LayerListProps = LayerListContainerProps & {
|
||||||
onMoveLayer: SortEndHandler
|
onMoveLayer: OnMoveLayerCallback
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class LayerList extends React.Component<LayerListProps> {
|
const LayerList: React.FC<LayerListProps> = (props) => {
|
||||||
render() {
|
const sensors = useSensors(useSensor(PointerSensor));
|
||||||
return <LayerListContainerSortable
|
|
||||||
{...this.props}
|
const handleDragEnd = (event: DragEndEvent) => {
|
||||||
helperClass='sortableHelper'
|
const {active, over} = event;
|
||||||
onSortEnd={this.props.onMoveLayer.bind(this)}
|
if (!over) return;
|
||||||
useDragHandle={true}
|
|
||||||
shouldCancelStart={() => false}
|
const oldIndex = props.layers.findIndex(layer => layer.id === active.id);
|
||||||
/>
|
const newIndex = props.layers.findIndex(layer => layer.id === over.id);
|
||||||
}
|
|
||||||
|
if (oldIndex !== -1 && newIndex !== -1 && oldIndex !== newIndex) {
|
||||||
|
props.onMoveLayer({oldIndex, newIndex});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const layerIds = props.layers.map(layer => layer.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||||
|
<SortableContext items={layerIds} strategy={verticalListSortingStrategy}>
|
||||||
|
<LayerListContainer {...props} />
|
||||||
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default LayerList;
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import {MdContentCopy, MdVisibility, MdVisibilityOff, MdDelete} from 'react-icons/md'
|
import {MdContentCopy, MdVisibility, MdVisibilityOff, MdDelete} from 'react-icons/md'
|
||||||
import { IconContext } from 'react-icons'
|
import { IconContext } from 'react-icons'
|
||||||
|
import {useSortable} from '@dnd-kit/sortable'
|
||||||
|
import {CSS} from '@dnd-kit/utilities'
|
||||||
|
|
||||||
import IconLayer from './IconLayer'
|
import IconLayer from './IconLayer'
|
||||||
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
|
|
||||||
|
|
||||||
|
|
||||||
type DraggableLabelProps = {
|
type DraggableLabelProps = {
|
||||||
layerId: string
|
layerId: string
|
||||||
layerType: string
|
layerType: string
|
||||||
|
dragAttributes?: React.HTMLAttributes<HTMLElement>
|
||||||
|
dragListeners?: React.HTMLAttributes<HTMLElement>
|
||||||
};
|
};
|
||||||
|
|
||||||
const DraggableLabel = SortableHandle((props: DraggableLabelProps) => {
|
const DraggableLabel: React.FC<DraggableLabelProps> = (props) => {
|
||||||
return <div className="maputnik-layer-list-item-handle">
|
const {dragAttributes, dragListeners} = props;
|
||||||
|
return <div className="maputnik-layer-list-item-handle" {...dragAttributes} {...dragListeners}>
|
||||||
<IconLayer
|
<IconLayer
|
||||||
className="layer-handle__icon"
|
className="layer-handle__icon"
|
||||||
type={props.layerType}
|
type={props.layerType}
|
||||||
@@ -23,7 +26,7 @@ const DraggableLabel = SortableHandle((props: DraggableLabelProps) => {
|
|||||||
{props.layerId}
|
{props.layerId}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
});
|
};
|
||||||
|
|
||||||
type IconActionProps = {
|
type IconActionProps = {
|
||||||
action: string
|
action: string
|
||||||
@@ -82,55 +85,80 @@ type LayerListItemProps = {
|
|||||||
onLayerVisibilityToggle?(...args: unknown[]): unknown
|
onLayerVisibilityToggle?(...args: unknown[]): unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
class LayerListItem extends React.Component<LayerListItemProps> {
|
const LayerListItem = React.forwardRef<HTMLLIElement, LayerListItemProps>((props, ref) => {
|
||||||
static defaultProps = {
|
const {
|
||||||
isSelected: false,
|
isSelected = false,
|
||||||
visibility: 'visible',
|
visibility = 'visible',
|
||||||
onLayerCopy: () => {},
|
onLayerCopy = () => {},
|
||||||
onLayerDestroy: () => {},
|
onLayerDestroy = () => {},
|
||||||
onLayerVisibilityToggle: () => {},
|
onLayerVisibilityToggle = () => {},
|
||||||
}
|
} = props;
|
||||||
|
|
||||||
render() {
|
const {
|
||||||
const visibilityAction = this.props.visibility === 'visible' ? 'show' : 'hide';
|
attributes,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
isDragging,
|
||||||
|
} = useSortable({id: props.layerId});
|
||||||
|
|
||||||
return <IconContext.Provider value={{size: '14px'}}>
|
const style = {
|
||||||
<li
|
transform: CSS.Transform.toString(transform),
|
||||||
id={this.props.id}
|
transition,
|
||||||
key={this.props.layerId}
|
opacity: isDragging ? 0.5 : 1,
|
||||||
onClick={_e => this.props.onLayerSelect(this.props.layerIndex)}
|
};
|
||||||
data-wd-key={"layer-list-item:"+this.props.layerId}
|
|
||||||
className={classnames({
|
|
||||||
"maputnik-layer-list-item": true,
|
|
||||||
"maputnik-layer-list-item-selected": this.props.isSelected,
|
|
||||||
[this.props.className!]: true,
|
|
||||||
})}>
|
|
||||||
<DraggableLabel {...this.props} />
|
|
||||||
<span style={{flexGrow: 1}} />
|
|
||||||
<IconAction
|
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
|
||||||
action={'delete'}
|
|
||||||
classBlockName="delete"
|
|
||||||
onClick={_e => this.props.onLayerDestroy!(this.props.layerIndex)}
|
|
||||||
/>
|
|
||||||
<IconAction
|
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":copy"}
|
|
||||||
action={'duplicate'}
|
|
||||||
classBlockName="duplicate"
|
|
||||||
onClick={_e => this.props.onLayerCopy!(this.props.layerIndex)}
|
|
||||||
/>
|
|
||||||
<IconAction
|
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":toggle-visibility"}
|
|
||||||
action={visibilityAction}
|
|
||||||
classBlockName="visibility"
|
|
||||||
classBlockModifier={visibilityAction}
|
|
||||||
onClick={_e => this.props.onLayerVisibilityToggle!(this.props.layerIndex)}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</IconContext.Provider>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const LayerListItemSortable = SortableElement<LayerListItemProps>((props: LayerListItemProps) => <LayerListItem {...props} />);
|
const visibilityAction = visibility === 'visible' ? 'show' : 'hide';
|
||||||
|
|
||||||
export default LayerListItemSortable;
|
// 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;
|
||||||
|
|
||||||
|
return <IconContext.Provider value={{size: '14px'}}>
|
||||||
|
<li
|
||||||
|
ref={(node) => {
|
||||||
|
setNodeRef(node);
|
||||||
|
if (refObject) {
|
||||||
|
refObject.current = node;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={style}
|
||||||
|
id={props.id}
|
||||||
|
onClick={_e => props.onLayerSelect(props.layerIndex)}
|
||||||
|
data-wd-key={"layer-list-item:" + props.layerId}
|
||||||
|
className={classnames({
|
||||||
|
"maputnik-layer-list-item": true,
|
||||||
|
"maputnik-layer-list-item-selected": isSelected,
|
||||||
|
[props.className!]: true,
|
||||||
|
})}>
|
||||||
|
<DraggableLabel
|
||||||
|
layerId={props.layerId}
|
||||||
|
layerType={props.layerType}
|
||||||
|
dragAttributes={attributes}
|
||||||
|
dragListeners={listeners}
|
||||||
|
/>
|
||||||
|
<span style={{flexGrow: 1}} />
|
||||||
|
<IconAction
|
||||||
|
wdKey={"layer-list-item:" + props.layerId+":delete"}
|
||||||
|
action={'delete'}
|
||||||
|
classBlockName="delete"
|
||||||
|
onClick={_e => onLayerDestroy!(props.layerIndex)}
|
||||||
|
/>
|
||||||
|
<IconAction
|
||||||
|
wdKey={"layer-list-item:" + props.layerId+":copy"}
|
||||||
|
action={'duplicate'}
|
||||||
|
classBlockName="duplicate"
|
||||||
|
onClick={_e => onLayerCopy!(props.layerIndex)}
|
||||||
|
/>
|
||||||
|
<IconAction
|
||||||
|
wdKey={"layer-list-item:"+props.layerId+":toggle-visibility"}
|
||||||
|
action={visibilityAction}
|
||||||
|
classBlockName="visibility"
|
||||||
|
classBlockModifier={visibilityAction}
|
||||||
|
onClick={_e => onLayerVisibilityToggle!(props.layerIndex)}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</IconContext.Provider>
|
||||||
|
});
|
||||||
|
|
||||||
|
export default LayerListItem;
|
||||||
|
|||||||
@@ -175,7 +175,6 @@ class ModalAddInternal extends React.Component<ModalAddInternalProps, ModalAddSt
|
|||||||
}
|
}
|
||||||
{!NON_SOURCE_LAYERS.includes(this.state.type) &&
|
{!NON_SOURCE_LAYERS.includes(this.state.type) &&
|
||||||
<FieldSourceLayer
|
<FieldSourceLayer
|
||||||
isFixed={true}
|
|
||||||
sourceLayerIds={layers}
|
sourceLayerIds={layers}
|
||||||
value={this.state['source-layer']}
|
value={this.state['source-layer']}
|
||||||
onChange={(v: string) => this.setState({ 'source-layer': v })}
|
onChange={(v: string) => this.setState({ 'source-layer': v })}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Slugify from 'slugify'
|
|||||||
import {saveAs} from 'file-saver'
|
import {saveAs} from 'file-saver'
|
||||||
import {version} from 'maplibre-gl/package.json'
|
import {version} from 'maplibre-gl/package.json'
|
||||||
import {format} from '@maplibre/maplibre-gl-style-spec'
|
import {format} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import type {StyleSpecification} from 'maplibre-gl'
|
|
||||||
import {MdMap, MdSave} from 'react-icons/md'
|
import {MdMap, MdSave} from 'react-icons/md'
|
||||||
import {WithTranslation, withTranslation} from 'react-i18next';
|
import {WithTranslation, withTranslation} from 'react-i18next';
|
||||||
|
|
||||||
@@ -12,6 +11,7 @@ import InputButton from './InputButton'
|
|||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
import style from '../libs/style'
|
import style from '../libs/style'
|
||||||
import fieldSpecAdditional from '../libs/field-spec-additional'
|
import fieldSpecAdditional from '../libs/field-spec-additional'
|
||||||
|
import type {OnStyleChangedCallback, StyleSpecificationWithId} from '../libs/definitions'
|
||||||
|
|
||||||
|
|
||||||
const MAPLIBRE_GL_VERSION = version;
|
const MAPLIBRE_GL_VERSION = version;
|
||||||
@@ -19,8 +19,8 @@ const showSaveFilePickerAvailable = typeof window.showSaveFilePicker === "functi
|
|||||||
|
|
||||||
|
|
||||||
type ModalExportInternalProps = {
|
type ModalExportInternalProps = {
|
||||||
mapStyle: StyleSpecification & { id: string }
|
mapStyle: StyleSpecificationWithId
|
||||||
onStyleChanged(...args: unknown[]): unknown
|
onStyleChanged: OnStyleChangedCallback
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onOpenToggle(...args: unknown[]): unknown
|
onOpenToggle(...args: unknown[]): unknown
|
||||||
onSetFileHandle(fileHandle: FileSystemFileHandle | null): unknown
|
onSetFileHandle(fileHandle: FileSystemFileHandle | null): unknown
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ import FieldEnum from './FieldEnum'
|
|||||||
import FieldColor from './FieldColor'
|
import FieldColor from './FieldColor'
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
import fieldSpecAdditional from '../libs/field-spec-additional'
|
import fieldSpecAdditional from '../libs/field-spec-additional'
|
||||||
|
import type {OnStyleChangedCallback, StyleSpecificationWithId} from '../libs/definitions';
|
||||||
|
|
||||||
type ModalSettingsInternalProps = {
|
type ModalSettingsInternalProps = {
|
||||||
mapStyle: StyleSpecification
|
mapStyle: StyleSpecificationWithId
|
||||||
onStyleChanged(...args: unknown[]): unknown
|
onStyleChanged: OnStyleChangedCallback
|
||||||
onChangeMetadataProperty(...args: unknown[]): unknown
|
onChangeMetadataProperty(...args: unknown[]): unknown
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onOpenToggle(...args: unknown[]): unknown
|
onOpenToggle(...args: unknown[]): unknown
|
||||||
@@ -62,7 +63,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
|
|||||||
changeTerrainProperty(property: keyof TerrainSpecification, value: any) {
|
changeTerrainProperty(property: keyof TerrainSpecification, value: any) {
|
||||||
const terrain = {
|
const terrain = {
|
||||||
...this.props.mapStyle.terrain,
|
...this.props.mapStyle.terrain,
|
||||||
}
|
} as TerrainSpecification;
|
||||||
|
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
delete terrain[property];
|
delete terrain[property];
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {MdAddCircleOutline, MdDelete} from 'react-icons/md'
|
import {MdAddCircleOutline, 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 type {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, StyleSpecification, VectorSourceSpecification} from 'maplibre-gl'
|
import type {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, VectorSourceSpecification} from 'maplibre-gl'
|
||||||
import { WithTranslation, withTranslation } from 'react-i18next';
|
import { WithTranslation, withTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
@@ -13,6 +13,7 @@ import ModalSourcesTypeEditor, { EditorMode } from './ModalSourcesTypeEditor'
|
|||||||
import style from '../libs/style'
|
import style from '../libs/style'
|
||||||
import { deleteSource, addSource, changeSource } from '../libs/source'
|
import { deleteSource, addSource, changeSource } from '../libs/source'
|
||||||
import publicSources from '../config/tilesets.json'
|
import publicSources from '../config/tilesets.json'
|
||||||
|
import { OnStyleChangedCallback, StyleSpecificationWithId } from '../libs/definitions';
|
||||||
|
|
||||||
|
|
||||||
type PublicSourceProps = {
|
type PublicSourceProps = {
|
||||||
@@ -270,10 +271,10 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ModalSourcesInternalProps = {
|
type ModalSourcesInternalProps = {
|
||||||
mapStyle: StyleSpecification
|
mapStyle: StyleSpecificationWithId
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onOpenToggle(...args: unknown[]): unknown
|
onOpenToggle(...args: unknown[]): unknown
|
||||||
onStyleChanged(...args: unknown[]): unknown
|
onStyleChanged: OnStyleChangedCallback
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
class ModalSourcesInternal extends React.Component<ModalSourcesInternalProps> {
|
class ModalSourcesInternal extends React.Component<ModalSourcesInternalProps> {
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
interface DebugStore {
|
|
||||||
[namespace: string]: {
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const debugStore: DebugStore = {};
|
|
||||||
|
|
||||||
function enabled() {
|
|
||||||
const qs = new URL(window.location.href).searchParams;
|
|
||||||
const debugQs = qs.get("debug");
|
|
||||||
if(debugQs) {
|
|
||||||
return !!debugQs.match(/^(|1|true)$/);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function genErr() {
|
|
||||||
return new Error("Debug not enabled, enable by appending '?debug' to your query string");
|
|
||||||
}
|
|
||||||
|
|
||||||
function set(namespace: keyof DebugStore, key: string, value: any) {
|
|
||||||
if(!enabled()) {
|
|
||||||
throw genErr();
|
|
||||||
}
|
|
||||||
debugStore[namespace] = debugStore[namespace] || {};
|
|
||||||
debugStore[namespace][key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get(namespace: keyof DebugStore, key: string) {
|
|
||||||
if(!enabled()) {
|
|
||||||
throw genErr();
|
|
||||||
}
|
|
||||||
if(Object.prototype.hasOwnProperty.call(debugStore, namespace)) {
|
|
||||||
return debugStore[namespace][key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mod = {
|
|
||||||
enabled,
|
|
||||||
get,
|
|
||||||
set
|
|
||||||
};
|
|
||||||
|
|
||||||
(window as any).debug = mod;
|
|
||||||
export default mod;
|
|
||||||
Vendored
+18
@@ -0,0 +1,18 @@
|
|||||||
|
import type { StyleSpecification } from "maplibre-gl";
|
||||||
|
|
||||||
|
export type StyleSpecificationWithId = StyleSpecification & {id: string};
|
||||||
|
|
||||||
|
export type OnStyleChangedOpts = {
|
||||||
|
save?: boolean;
|
||||||
|
addRevision?: boolean;
|
||||||
|
initialLoad?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OnStyleChangedCallback = (newStyle: StyleSpecificationWithId, opts: OnStyleChangedOpts={}) => void;
|
||||||
|
|
||||||
|
export type OnMoveLayerCallback = (move: {oldIndex: number; newIndex: number}) => void;
|
||||||
|
|
||||||
|
export interface IStyleStore {
|
||||||
|
getLatestStyle(): Promise<StyleSpecificationWithId>;
|
||||||
|
save(mapStyle: StyleSpecificationWithId): StyleSpecificationWithId;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type {StyleSpecification} from "maplibre-gl";
|
import { StyleSpecificationWithId } from "./definitions";
|
||||||
|
|
||||||
export class RevisionStore {
|
export class RevisionStore {
|
||||||
revisions: (StyleSpecification & {id: string})[];
|
revisions: StyleSpecificationWithId[];
|
||||||
currentIdx: number;
|
currentIdx: number;
|
||||||
|
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export class RevisionStore {
|
|||||||
return this.revisions[this.currentIdx]
|
return this.revisions[this.currentIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
addRevision(revision: StyleSpecification & {id: string}) {
|
addRevision(revision: StyleSpecificationWithId) {
|
||||||
// clear any "redo" revisions once a change is made
|
// clear any "redo" revisions once a change is made
|
||||||
// and ensure current index is at end of list
|
// and ensure current index is at end of list
|
||||||
this.revisions = this.revisions.slice(0, this.currentIdx + 1);
|
this.revisions = this.revisions.slice(0, this.currentIdx + 1);
|
||||||
|
|||||||
+5
-4
@@ -1,6 +1,7 @@
|
|||||||
import type {StyleSpecification, SourceSpecification} from "maplibre-gl";
|
import type {SourceSpecification} from "maplibre-gl";
|
||||||
|
import type {StyleSpecificationWithId} from "./definitions";
|
||||||
|
|
||||||
export function deleteSource(mapStyle: StyleSpecification, sourceId: string) {
|
export function deleteSource(mapStyle: StyleSpecificationWithId, sourceId: string) {
|
||||||
const remainingSources = { ...mapStyle.sources}
|
const remainingSources = { ...mapStyle.sources}
|
||||||
delete remainingSources[sourceId]
|
delete remainingSources[sourceId]
|
||||||
return {
|
return {
|
||||||
@@ -10,11 +11,11 @@ export function deleteSource(mapStyle: StyleSpecification, sourceId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function addSource(mapStyle: StyleSpecification, sourceId: string, source: SourceSpecification) {
|
export function addSource(mapStyle: StyleSpecificationWithId, sourceId: string, source: SourceSpecification) {
|
||||||
return changeSource(mapStyle, sourceId, source)
|
return changeSource(mapStyle, sourceId, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changeSource(mapStyle: StyleSpecification, sourceId: string, source: SourceSpecification) {
|
export function changeSource(mapStyle: StyleSpecificationWithId, sourceId: string, source: SourceSpecification) {
|
||||||
const changedSources = {
|
const changedSources = {
|
||||||
...mapStyle.sources,
|
...mapStyle.sources,
|
||||||
[sourceId]: source
|
[sourceId]: source
|
||||||
|
|||||||
@@ -1,46 +1,38 @@
|
|||||||
import style from './style.js'
|
import style from '../style'
|
||||||
import {format} from '@maplibre/maplibre-gl-style-spec'
|
import {format} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import type {StyleSpecification} from 'maplibre-gl'
|
|
||||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||||
|
import type {IStyleStore, OnStyleChangedCallback, StyleSpecificationWithId} from '../definitions'
|
||||||
|
|
||||||
export type ApiStyleStoreOptions = {
|
export type ApiStyleStoreOptions = {
|
||||||
port: string | null
|
onLocalStyleChange?: OnStyleChangedCallback
|
||||||
host: string | null
|
|
||||||
onLocalStyleChange?: (style: any) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ApiStyleStore {
|
export class ApiStyleStore implements IStyleStore {
|
||||||
|
|
||||||
localUrl: string;
|
localUrl: string;
|
||||||
websocketUrl: string;
|
websocketUrl: string;
|
||||||
latestStyleId: string | undefined = undefined;
|
latestStyleId: string | undefined = undefined;
|
||||||
onLocalStyleChange: (style: any) => void;
|
onLocalStyleChange: OnStyleChangedCallback;
|
||||||
|
|
||||||
constructor(opts: ApiStyleStoreOptions) {
|
constructor(opts: ApiStyleStoreOptions) {
|
||||||
this.onLocalStyleChange = opts.onLocalStyleChange || (() => {})
|
this.onLocalStyleChange = opts.onLocalStyleChange || (() => {})
|
||||||
const port = opts.port || '8000'
|
const port = window.location.port
|
||||||
const host = opts.host || 'localhost'
|
const host = 'localhost'
|
||||||
this.localUrl = `http://${host}:${port}`
|
this.localUrl = `http://${host}:${port}`
|
||||||
this.websocketUrl = `ws://${host}:${port}/ws`
|
this.websocketUrl = `ws://${host}:${port}/ws`
|
||||||
this.init = this.init.bind(this)
|
this.init = this.init.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb: (...args: any[]) => void) {
|
async init(): Promise<void> {
|
||||||
fetch(this.localUrl + '/styles', {
|
try {
|
||||||
mode: 'cors',
|
const response = await fetch(this.localUrl + '/styles', {mode: 'cors'});
|
||||||
})
|
const body = await response.json();
|
||||||
.then((response) => {
|
const styleIds = body;
|
||||||
return response.json();
|
this.latestStyleId = styleIds[0]
|
||||||
})
|
this.notifyLocalChanges();
|
||||||
.then((body) => {
|
} catch {
|
||||||
const styleIds = body;
|
throw new Error('Can not connect to style API');
|
||||||
this.latestStyleId = styleIds[0]
|
}
|
||||||
this.notifyLocalChanges()
|
|
||||||
cb(null)
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
cb(new Error('Can not connect to style API'))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyLocalChanges() {
|
notifyLocalChanges() {
|
||||||
@@ -59,24 +51,20 @@ export class ApiStyleStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
latestStyle(cb: (...args: any[]) => void) {
|
async getLatestStyle(): Promise<StyleSpecificationWithId> {
|
||||||
if(this.latestStyleId) {
|
if(this.latestStyleId) {
|
||||||
fetch(this.localUrl + '/styles/' + this.latestStyleId, {
|
const response = await fetch(this.localUrl + '/styles/' + this.latestStyleId, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
})
|
});
|
||||||
.then(function(response) {
|
const body = await response.json();
|
||||||
return response.json();
|
return style.ensureStyleValidity(body);
|
||||||
})
|
|
||||||
.then(function(body) {
|
|
||||||
cb(style.ensureStyleValidity(body))
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No latest style available. You need to init the api backend first.')
|
throw new Error('No latest style available. You need to init the api backend first.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save current style replacing previous version
|
// Save current style replacing previous version
|
||||||
save(mapStyle: StyleSpecification & { id: string }) {
|
save(mapStyle: StyleSpecificationWithId) {
|
||||||
const styleJSON = format(
|
const styleJSON = format(
|
||||||
style.stripAccessTokens(
|
style.stripAccessTokens(
|
||||||
style.replaceAccessTokens(mapStyle)
|
style.replaceAccessTokens(mapStyle)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
import { IStyleStore, OnStyleChangedCallback } from "../definitions";
|
||||||
|
import { getStyleUrlFromAddressbarAndRemoveItIfNeeded, loadStyleUrl } from "../urlopen";
|
||||||
|
import { ApiStyleStore } from "./apistore";
|
||||||
|
import { StyleStore } from "./stylestore";
|
||||||
|
|
||||||
|
export async function createStyleStore(onStyleChanged: OnStyleChangedCallback): Promise<IStyleStore> {
|
||||||
|
const styleUrl = getStyleUrlFromAddressbarAndRemoveItIfNeeded();
|
||||||
|
const useStyleUrl = styleUrl && window.confirm("Load style from URL: " + styleUrl + " and discard current changes?");
|
||||||
|
let styleStore: IStyleStore;
|
||||||
|
if (import.meta.env.MODE === 'desktop' && !useStyleUrl) {
|
||||||
|
const apiStyleStore = new ApiStyleStore({
|
||||||
|
onLocalStyleChange: mapStyle => onStyleChanged(mapStyle, {save: false}),
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await apiStyleStore.init();
|
||||||
|
styleStore = apiStyleStore;
|
||||||
|
} catch {
|
||||||
|
styleStore = new StyleStore();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
styleStore = new StyleStore();
|
||||||
|
}
|
||||||
|
const styleToLoad = useStyleUrl ? await loadStyleUrl(styleUrl) : await styleStore.getLatestStyle();
|
||||||
|
onStyleChanged(styleToLoad, {initialLoad: true, save: false});
|
||||||
|
return styleStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { IStyleStore };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import style from './style'
|
import style from '../style'
|
||||||
import {loadStyleUrl} from './urlopen'
|
import {loadStyleUrl} from '../urlopen'
|
||||||
import publicSources from '../config/styles.json'
|
import publicSources from '../../config/styles.json'
|
||||||
import type {StyleSpecification} from 'maplibre-gl'
|
import type {IStyleStore, StyleSpecificationWithId} from '../definitions'
|
||||||
|
|
||||||
const storagePrefix = "maputnik"
|
const storagePrefix = "maputnik"
|
||||||
const stylePrefix = 'style'
|
const stylePrefix = 'style'
|
||||||
@@ -13,8 +13,8 @@ const storageKeys = {
|
|||||||
const defaultStyleUrl = publicSources[0].url
|
const defaultStyleUrl = publicSources[0].url
|
||||||
|
|
||||||
// Fetch a default style via URL and return it or a fallback style via callback
|
// Fetch a default style via URL and return it or a fallback style via callback
|
||||||
export function loadDefaultStyle(cb: (...args: any[]) => void) {
|
export function loadDefaultStyle(): Promise<StyleSpecificationWithId> {
|
||||||
loadStyleUrl(defaultStyleUrl, cb)
|
return loadStyleUrl(defaultStyleUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return style ids and dates of all styles stored in local storage
|
// Return style ids and dates of all styles stored in local storage
|
||||||
@@ -51,7 +51,7 @@ function styleKey(styleId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Manages many possible styles that are stored in the local storage
|
// Manages many possible styles that are stored in the local storage
|
||||||
export class StyleStore {
|
export class StyleStore implements IStyleStore {
|
||||||
/**
|
/**
|
||||||
* List of style ids
|
* List of style ids
|
||||||
*/
|
*/
|
||||||
@@ -63,10 +63,6 @@ export class StyleStore {
|
|||||||
this.mapStyles = loadStoredStyles();
|
this.mapStyles = loadStoredStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb: (...args: any[]) => void) {
|
|
||||||
cb(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete entire style history
|
// Delete entire style history
|
||||||
purge() {
|
purge() {
|
||||||
for (let i = 0; i < window.localStorage.length; i++) {
|
for (let i = 0; i < window.localStorage.length; i++) {
|
||||||
@@ -78,17 +74,21 @@ export class StyleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the last edited style
|
// Find the last edited style
|
||||||
latestStyle(cb: (...args: any[]) => void) {
|
async getLatestStyle(): Promise<StyleSpecificationWithId> {
|
||||||
if(this.mapStyles.length === 0) return loadDefaultStyle(cb)
|
if(this.mapStyles.length === 0) {
|
||||||
|
return loadDefaultStyle();
|
||||||
|
}
|
||||||
const styleId = window.localStorage.getItem(storageKeys.latest) as string;
|
const styleId = window.localStorage.getItem(storageKeys.latest) as string;
|
||||||
const styleItem = window.localStorage.getItem(styleKey(styleId))
|
const styleItem = window.localStorage.getItem(styleKey(styleId))
|
||||||
|
|
||||||
if(styleItem) return cb(JSON.parse(styleItem))
|
if (styleItem) {
|
||||||
loadDefaultStyle(cb)
|
return JSON.parse(styleItem) as StyleSpecificationWithId;
|
||||||
|
}
|
||||||
|
return loadDefaultStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save current style replacing previous version
|
// Save current style replacing previous version
|
||||||
save(mapStyle: StyleSpecification & { id: string }) {
|
save(mapStyle: StyleSpecificationWithId) {
|
||||||
mapStyle = style.ensureStyleValidity(mapStyle)
|
mapStyle = style.ensureStyleValidity(mapStyle)
|
||||||
const key = styleKey(mapStyle.id)
|
const key = styleKey(mapStyle.id)
|
||||||
|
|
||||||
+6
-6
@@ -1,6 +1,7 @@
|
|||||||
import {derefLayers} from '@maplibre/maplibre-gl-style-spec'
|
import {derefLayers} from '@maplibre/maplibre-gl-style-spec'
|
||||||
import type {StyleSpecification, LayerSpecification} from 'maplibre-gl'
|
import type {StyleSpecification, LayerSpecification} from 'maplibre-gl'
|
||||||
import tokens from '../config/tokens.json'
|
import tokens from '../config/tokens.json'
|
||||||
|
import type {StyleSpecificationWithId} from './definitions'
|
||||||
|
|
||||||
// Empty style is always used if no style could be restored or fetched
|
// Empty style is always used if no style could be restored or fetched
|
||||||
const emptyStyle = ensureStyleValidity({
|
const emptyStyle = ensureStyleValidity({
|
||||||
@@ -13,15 +14,14 @@ function generateId() {
|
|||||||
return Math.random().toString(36).substring(2, 9)
|
return Math.random().toString(36).substring(2, 9)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasId(style: StyleSpecification & { id?: string }): StyleSpecification & { id: string } {
|
function ensureHasId(style: StyleSpecification & { id?: string }): StyleSpecificationWithId {
|
||||||
if(!('id' in style) || !style.id) {
|
if(!('id' in style) || !style.id) {
|
||||||
style.id = generateId();
|
style.id = generateId();
|
||||||
return style as StyleSpecification & { id: string };
|
|
||||||
}
|
}
|
||||||
return style as StyleSpecification & { id: string };
|
return style as StyleSpecificationWithId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasNoInteractive(style: StyleSpecification & {id: string}) {
|
function ensureHasNoInteractive(style: StyleSpecificationWithId) {
|
||||||
const changedLayers = style.layers.map(layer => {
|
const changedLayers = style.layers.map(layer => {
|
||||||
const changedLayer: LayerSpecification & { interactive?: any } = { ...layer }
|
const changedLayer: LayerSpecification & { interactive?: any } = { ...layer }
|
||||||
delete changedLayer.interactive
|
delete changedLayer.interactive
|
||||||
@@ -34,14 +34,14 @@ function ensureHasNoInteractive(style: StyleSpecification & {id: string}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureHasNoRefs(style: StyleSpecification & {id: string}) {
|
function ensureHasNoRefs(style: StyleSpecificationWithId) {
|
||||||
return {
|
return {
|
||||||
...style,
|
...style,
|
||||||
layers: derefLayers(style.layers)
|
layers: derefLayers(style.layers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureStyleValidity(style: StyleSpecification): StyleSpecification & { id: string } {
|
function ensureStyleValidity(style: StyleSpecification): StyleSpecificationWithId {
|
||||||
return ensureHasNoInteractive(ensureHasNoRefs(ensureHasId(style)))
|
return ensureHasNoInteractive(ensureHasNoRefs(ensureHasId(style)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-23
@@ -1,30 +1,27 @@
|
|||||||
import style from './style'
|
import style from './style'
|
||||||
|
import { StyleSpecificationWithId } from './definitions';
|
||||||
|
|
||||||
export function initialStyleUrl() {
|
export function getStyleUrlFromAddressbarAndRemoveItIfNeeded(): string | null {
|
||||||
const initialUrl = new URL(window.location.href);
|
const initialUrl = new URL(window.location.href);
|
||||||
return initialUrl.searchParams.get('style');
|
const styleUrl = initialUrl.searchParams.get('style');
|
||||||
|
if (styleUrl) {
|
||||||
|
initialUrl.searchParams.delete('style');
|
||||||
|
window.history.replaceState({}, document.title, initialUrl.toString())
|
||||||
|
}
|
||||||
|
return styleUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadStyleUrl(styleUrl: string, cb: (...args: any[]) => void) {
|
export async function loadStyleUrl(styleUrl: string): Promise<StyleSpecificationWithId> {
|
||||||
console.log('Loading style', styleUrl)
|
console.log('Loading style', styleUrl)
|
||||||
fetch(styleUrl, {
|
try {
|
||||||
mode: 'cors',
|
const response = await fetch(styleUrl, {
|
||||||
credentials: "same-origin"
|
mode: 'cors',
|
||||||
})
|
credentials: "same-origin"
|
||||||
.then(function(response) {
|
});
|
||||||
return response.json();
|
const body = await response.json();
|
||||||
})
|
return style.ensureStyleValidity(body);
|
||||||
.then(function(body) {
|
} catch {
|
||||||
cb(style.ensureStyleValidity(body))
|
console.warn('Could not fetch default style: ' + styleUrl)
|
||||||
})
|
return style.emptyStyle
|
||||||
.catch(function() {
|
}
|
||||||
console.warn('Could not fetch default style', styleUrl)
|
|
||||||
cb(style.emptyStyle)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeStyleQuerystring() {
|
|
||||||
const initialUrl = new URL(window.location.href);
|
|
||||||
initialUrl.searchParams.delete('style');
|
|
||||||
window.history.replaceState({}, document.title, initialUrl.toString())
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -3,7 +3,7 @@ import react from "@vitejs/plugin-react";
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import istanbul from "vite-plugin-istanbul";
|
import istanbul from "vite-plugin-istanbul";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig(({ mode }) => ({
|
||||||
server: {
|
server: {
|
||||||
port: 8888,
|
port: 8888,
|
||||||
},
|
},
|
||||||
@@ -27,7 +27,8 @@ export default defineConfig({
|
|||||||
forceBuildInstrument: true, //Instrument the source code for cypress runs
|
forceBuildInstrument: true, //Instrument the source code for cypress runs
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
base: mode === "desktop" ? "/" : "/maputnik/",
|
||||||
define: {
|
define: {
|
||||||
global: "window",
|
global: "window"
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user